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