1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
2 
3 #include "aliases.h"
4 
5 #include "utf.h"
6 #include "query.h"
7 #include "address.h"
8 #include "mailbox.h"
9 #include "transaction.h"
10 #include "helperrowcreator.h"
11 
12 #include <stdio.h>
13 
14 
15 static AoxFactory<ListAliases>
16 f( "list", "aliases", "Display delivery aliases.",
17    "    Synopsis: aox list aliases [pattern]\n\n"
18    "    Displays a list of aliases where either the address or the\n"
19    "    target mailbox matches the specified shell glob pattern.\n"
20    "    Without a pattern, all aliases are listed.\n\n"
21    "    ls is an acceptable abbreviation for list.\n\n"
22    "    Examples:\n\n"
23    "      aox list aliases\n"
24    "      aox ls aliases /users/\\*\n" );
25 
26 
27 
28 /*! \class ListAliases aliases.h
29     This class handles the "aox list aliases" command.
30 */
31 
ListAliases(EStringList * args)32 ListAliases::ListAliases( EStringList * args )
33     : AoxCommand( args ), q( 0 )
34 {
35 }
36 
37 
execute()38 void ListAliases::execute()
39 {
40     if ( !q ) {
41         Utf8Codec c;
42         UString pattern = c.toUnicode( next() );
43         end();
44 
45         if ( !c.valid() )
46             error( "Argument encoding: " + c.error() );
47 
48         database();
49         EString s( "select (localpart||'@'||domain)::text as address, m.name "
50                   "from aliases join addresses a on (address=a.id) "
51                   "join mailboxes m on (mailbox=m.id)" );
52         if ( !pattern.isEmpty() )
53             s.append( " where localpart||'@'||domain like $1 or "
54                       "m.name like $1" );
55         q = new Query( s, this );
56         if ( !pattern.isEmpty() )
57             q->bind( 1, sqlPattern( pattern ) );
58         q->execute();
59     }
60 
61     while ( q->hasResults() ) {
62         Row * r = q->nextRow();
63         printf( "%s: %s\n",
64                 r->getEString( "address" ).cstr(),
65                 r->getUString( "name" ).utf8().cstr() );
66     }
67 
68     if ( !q->done() )
69         return;
70 
71     finish();
72 }
73 
74 
75 static AoxFactory<CreateAlias>
76 f2( "create", "alias", "Create a delivery alias.",
77     "    Synopsis: aox add alias <address> <destination>\n\n"
78     "    Creates an alias that instructs the L/SMTP server to accept\n"
79     "    mail to a given address, and deliver it to a given mailbox.\n"
80     "    The destination mailbox can be specified by name (starting\n"
81     "    with '/') or by email address (ie. creating another alias for\n"
82     "    the same mailbox).\n" );
83 
84 
85 class CreateAliasData
86     : public Garbage
87 {
88 public:
CreateAliasData()89     CreateAliasData()
90         : address( 0 ), mailbox( 0 ), t( 0 ), q( 0 )
91     {}
92 
93     Address * address;
94     Address * destination;
95     UString mailboxName;
96     Mailbox * mailbox;
97     Transaction * t;
98     Query * q;
99 };
100 
101 
102 /*! \class CreateAlias aliases.h
103     This class handles the "aox add alias" command.
104 */
105 
CreateAlias(EStringList * args)106 CreateAlias::CreateAlias( EStringList * args )
107     : AoxCommand( args ), d( new CreateAliasData )
108 {
109 }
110 
111 
execute()112 void CreateAlias::execute()
113 {
114     if ( !d->address ) {
115         Utf8Codec c;
116         parseOptions();
117         d->address = nextAsAddress();
118         EString * first = args()->firstElement();
119         if ( first && !first->startsWith( "/" ) && first->contains( "@" ) )
120             d->destination = nextAsAddress();
121         else
122             d->mailboxName = c.toUnicode( next() );
123         end();
124 
125         if ( !c.valid() )
126             error( "Argument encoding: " + c.error() );
127 
128         database( true );
129         if ( !d->mailboxName.isEmpty() )
130             Mailbox::setup( this );
131     }
132 
133     if ( !choresDone() )
134         return;
135 
136     if ( !d->t ) {
137         if ( !d->mailboxName.isEmpty() ) {
138             d->mailbox = Mailbox::obtain( d->mailboxName, false );
139             if ( !d->mailbox || d->mailbox->deleted() )
140                 error( "No mailbox named " + d->mailboxName.utf8() );
141         }
142 
143         d->t = new Transaction( this );
144         List< Address > l;
145         l.append( d->address );
146         if ( d->destination )
147             l.append( d->destination );
148         AddressCreator * ac = new AddressCreator( &l, d->t );
149         ac->execute();
150     }
151 
152     if ( !d->address->id() || ( d->destination && !d->destination->id() ) )
153         return;
154 
155     if ( !d->q ) {
156         if ( d->destination ) {
157             d->q = new Query( "insert into aliases (address, mailbox) "
158                               "select $1, mailbox from aliases al "
159                               "join addresses a on (al.address=a.id) "
160                               "where a.localpart=$2"
161                               " and a.domain=$3 "
162                               "limit 1",
163                               this );
164             d->q->bind( 1, d->address->id() );
165             d->q->bind( 2, d->destination->localpart() );
166             d->q->bind( 3, d->destination->domain() );
167         }
168         else {
169             d->q = new Query( "insert into aliases (address, mailbox) "
170                               "values ($1, $2)", this );
171             d->q->bind( 1, d->address->id() );
172             d->q->bind( 2, d->mailbox->id() );
173         }
174         d->t->enqueue( d->q );
175         d->t->execute();
176     }
177 
178     if ( !d->q->done() )
179         return;
180 
181     if ( d->q->failed() )
182         error( "Couldn't create alias: " + d->q->error() );
183 
184     if ( d->q->rows() < 1 )
185         error( "Could not locate destination for alias" );
186     else if ( d->q->rows() > 1 )
187         error( "Internal error: Inserted " + fn( d->q->rows() ) +
188                " instead of 1. Not committing." );
189 
190     d->t->commit();
191 
192     finish();
193 }
194 
195 
196 static AoxFactory<DeleteAlias>
197 f3( "delete", "alias", "Delete a delivery alias.",
198     "    Synopsis: aox delete alias <address> (<address>\n\n"
199     "    Deletes the alias that associated the specified address\n"
200     "    with a mailbox. If two addresses are specified, then the\n"
201     "    the alias is not deleted as a whole. Only the second address\n"
202     "    is removed from the alias.\n" );
203 
204 
205 /*! \class DeleteAlias aliases.h
206     This class handles the "aox delete alias" command.
207 */
208 
DeleteAlias(EStringList * args)209 DeleteAlias::DeleteAlias( EStringList * args )
210     : AoxCommand( args ), q( 0 )
211 {
212 }
213 
214 
execute()215 void DeleteAlias::execute()
216 {
217     if ( !q ) {
218         parseOptions();
219         Address * address = nextAsAddress();
220         Address * target = 0;
221         if ( !args()->isEmpty() )
222             target = nextAsAddress();
223         end();
224 
225         database( true );
226         EString rm = "delete from aliases where address=any(select a.id "
227                      "from addresses a "
228                      "join aliases al on (a.id=al.address) "
229                      "where a.localpart=$1 and a.domain=$2)";
230         EString tx = " and mailbox=any(select mb.id from mailboxes mb "
231                      "join aliases al on (mb.id=a.mailbox) "
232                      "join addresses a on (al.address=a.id) "
233                      "where a.localpart=$3 and a.domain=$4)";
234         if ( target )
235             q = new Query( rm + tx, this );
236         else
237             q = new Query( rm, this );
238         q->bind( 1, address->localpart() );
239         q->bind( 2, address->domain() );
240         if ( target ) {
241             q->bind( 3, target->localpart() );
242             q->bind( 4, target->domain() );
243         }
244         q->execute();
245     }
246 
247     if ( !q->done() )
248         return;
249 
250     if ( q->failed() )
251         error( "Couldn't delete alias: " + q->error() );
252 
253     finish();
254 }
255