62
61
add_data_storage(STORE_FORWARD,retrieve_aliases,restore_aliases);
64
static array|void get_parent_groups(object group)
66
object parent = group->get_parent();
71
parents += ({ parent });
72
array grandparents=get_parent_groups(parent);
73
if(arrayp(grandparents) )
74
parents += grandparents;
78
static array get_user_groups(object user)
81
array usergroups = groups = user->get_groups();
82
foreach(usergroups;; object group)
84
array parents = get_parent_groups(group);
88
return(Array.uniq(groups));
93
add_global_event(EVENT_ADD_MEMBER, user_join_group, PHASE_NOTIFY);
95
LOG_FORWARD("forwards are: %O\n", mForwards);
96
foreach(mForwards; string user;)
98
groups += get_user_groups(MODULE_USERS->lookup(user));
100
groups = Array.uniq(groups);
101
LOG_FORWARD("listen to groups: %O\n", groups->get_identifier());
102
foreach(groups;; object group)
104
listen_annotations(group);
106
// always provide an abuse adress:
107
add_alias( "abuse", "admin" );
110
static void user_join_group(int event, object group, object caller, object user)
112
if(!objectp(mListeners[group]) && mForwards[user->get_user_name()])
113
listen_annotations(group);
116
void listen_annotations(object group)
118
if(!objectp(mListeners[group]))
121
add_event(group->query_attribute(GROUP_WORKROOM),
122
EVENT_ANNOTATE|EVENTS_MONITORED, PHASE_NOTIFY,
123
lambda(int event, mixed ... args)
125
send_annotation_remote(event, group, @args);
130
werror("already listening for %s in %s:\n%O", group->get_identifier(),
131
group->query_attribute(GROUP_WORKROOM)->get_identifier(),
132
mListeners[group]->get_listening());
136
static void send_annotation_remote(int event, object group, object annotated,
137
object caller, object ... thread)
139
// find root of thread...
140
object parent = thread[-1];
141
while ( objectp(parent->get_annotating()) )
143
parent = parent->get_annotating();
146
LOG_FORWARD("send_annotation_remote(event: %d, group: %O, annotated: %O, caller: %O, thread: %s) - parent: %O\n",
147
event, group->get_identifier(), annotated->get_identifier(),
148
caller->get_identifier(), thread->get_identifier()*", ",
149
parent->get_identifier());
151
if(parent==annotated)
152
send_group(group, thread[-1]);
65
155
void install_module()
67
157
_mailserver = _Server->query_config(CFG_MAILSERVER);
266
* split targets into remote, groups, users, and objects
268
* @author Martin B�hr
269
* @param array(string) names - the addresses to work with
270
* @return mapping - containing the different recipient types
272
private mapping resolve_recipients(array(string) names)
274
array unresolved=({});
275
array(string) resolved=replace_aliases(names);
276
resolved=replace_forwards(resolved);
278
mapping result =([ "groups":({}), "remote":({}), "users":({}),
283
foreach(resolved;; string target)
285
if ( !stringp(target) )
287
if(search(target,"@")!=-1)
288
result->remote += ({ target });
289
else if ( objectp(target_obj=MODULE_GROUPS->lookup(target)) )
290
result->groups += ({ target_obj });
291
// groupnames may have spaces, but those don't work well with email.
292
else if ( objectp(target_obj=MODULE_GROUPS->lookup(replace(target, "-", " "))) )
293
result->groups += ({ target_obj });
294
else if ( objectp(target_obj=MODULE_USERS->lookup(target)) )
295
result->users += ({ target_obj });
296
else if ( sscanf(target,"%d",oid)
297
&& objectp(target_obj=_Database->find_object(oid)) )
298
result->objects += ({ target_obj });
300
unresolved += ({ target });
303
if(sizeof(unresolved))
304
FATAL("Warning! unresolved addresses: %O", unresolved);
164
309
* within an array of adresses, search for aliases and replace them with
282
421
int send_message_raw(array(string) target, string rawText, string envFrom)
424
LOG_FORWARD("Forward: send_message_raw(%O)", target);
425
mapping sendAs = resolve_recipients(target);
285
426
int res=1; //success
287
array(string) resolved=replace_aliases(target);
288
MESSAGE("Aliases replaced");
289
resolved=replace_forwards(resolved);
290
MESSAGE("Forwards are: %O", resolved);
291
array(string) asRemote=get_remote_addresses(Array.uniq(resolved));
292
MESSAGE("Remote adresses are: %O", asRemote);
293
array(string) asLocal=resolved-asRemote;
295
MESSAGE("Local addresses are: %O", asLocal);
296
if(sizeof(asLocal)>0) hasLocal=1;
298
err = catch(res=send_local(asLocal,Messaging.MIME2Message(rawText)));
301
FATAL("Error while sending message local: %O", err);
304
LOG_FORWARD("Warning! send_message_raw failed on one ore more local recipients!");
306
for(int i=0;i<sizeof(asRemote);i++)
307
send_remote(asRemote[i],rawText,envFrom);
427
if(sizeof(sendAs->users))
428
err = catch(res=send_users(sendAs->users,Messaging.MIME2Message(rawText)));
430
FATAL("Error while sending message to users: %O, %O", err[0], err[1]);
432
LOG_FORWARD("Warning! send_message_raw failed on one ore more recipient users!");
435
if(sizeof(sendAs->objects))
436
err = catch(res=send_objects(sendAs->objects,Messaging.MIME2Message(rawText)));
438
FATAL("Error while sending message to objects: %O,%O", err[0], err[1]);
441
LOG_FORWARD("Warning! send_message_raw failed on one ore more recipient objects!");
443
send_remote(sendAs->remote, rawText, envFrom);
444
foreach(sendAs->groups;; object target)
445
send_group(target,rawText,envFrom);
451
* send a message to objects
453
* @author Martin B�hr
454
* @param array(object) targets - the objects to send the message to
455
* @param string msg - the message to send
456
* @return int 1 if successful
458
private int send_objects(array(object) targets, Messaging.Message msg)
460
LOG_FORWARD("send_objects(%O, %O)\n", targets, msg->header()->subject);
461
if(sizeof(targets)==0 || !arrayp(targets)) return 0;
465
seteuid(USER("root"));
466
foreach(targets; int count; object target)
468
Messaging.Message copy;
469
if(count<sizeof(targets)-1)
470
// duplicate message if more than one recipient
471
copy=msg->duplicate();
473
// last recipient gets original
476
mixed err=catch{Messaging.add_message_to_object(copy,target);};
482
FATAL("unable to add message to: %s(%d):%O", target->get_identifier(), target->get_object_id(), err);
488
LOG_FORWARD("Warning!! send_objects encountered errors - some recipients failed!");
496
* send a message to users
498
* @author Martin B�hr
499
* @param array(object) users - the users to send the message to
500
* @param string msg - the message to send
501
* @return int 1 if successful
503
private int send_users(array(object) targets, Messaging.Message msg)
505
if(sizeof(targets)==0 || !arrayp(targets)) return 0;
507
foreach(targets; int count; object user)
509
if ( !objectp(user) )
511
Messaging.Message copy=msg->duplicate();
512
if(count<sizeof(targets)-1)
513
// duplicate message if more than one recipient
514
copy=msg->duplicate();
516
// last recipient gets original
519
//the recipient gets all rights on his/her copy
520
copy->grant_access(user);
522
Messaging.BaseMailBox box = Messaging.get_mailbox(user);
523
copy->this()->set_acquire(box->this());
524
box->add_message(copy);
532
* create headers to be added to annotations sent as mails
534
* @author Martin B�hr
535
* @param object group - the group to send the message to
536
* @return mapping of headers
538
mapping create_list_headers(object group)
540
mapping headers = ([]);
541
headers["X-Steam-Group"] = group->get_identifier();
542
headers["List-Id"] = replace(group->get_identifier(), " ", "-")+"@"+
543
(_Server->query_config("smtp_host")||(_Server->query_config("machine")+"."+_Server->query_config("domain")));
545
object group_workroom = group->query_attribute("GROUP_WORKROOM");
546
object modpath=get_module("filepath:tree");
547
headers["X-Steam-Path"] = _Server->query_config("web_server")+
548
modpath->object_to_filename(group_workroom);
550
headers["X-Steam-Annotates"] = _Server->query_config("web_server")+":"
551
+(string)group_workroom->get_object_id();
556
* send a message to a group
558
* @author Martin B�hr
559
* @param object group - the group to send the message to
560
* @param string|object msg - the message to send
561
* @return int 1 if successful
563
private int send_group(object group, string|object msg, string|void envFrom)
565
LOG_FORWARD("send_group(%O)\n", group);
566
mapping headers = create_list_headers(group);
571
rawmessage = (((array)headers)[*]*": ")*"\r\n" + "\r\n" + msg;
572
messageobj = Messaging.MIME2Message(rawmessage);
574
//string messages come from outside, need to be added to group
575
send_objects( ({ group->query_attribute(GROUP_WORKROOM) }), messageobj);
577
else if(objectp(msg))
579
messageobj = Messaging.Message(msg);
581
envFrom = "<"+messageobj->sender()+">";
582
LOG_FORWARD("sender is: %O\n", messageobj->sender());
583
messageobj->add_to_header(headers);
584
messageobj->add_to_header(([ "to":headers["List-Id"] ]));
585
rawmessage = (string)messageobj->mime_message();
589
FATAL("Warning! unknown message format: %O", msg);
593
//TODO: only send to users that want a copy of group mails
594
array(string) members = group->get_members_recursive(CLASS_USER)->get_user_name();
595
send_message_raw(members, rawmessage, envFrom);
601
* send a message to a subgroup
603
* @author Martin B�hr
604
* @param object group - the group to send the message to
605
* @param object parent - the group that the message was initially sent to
606
* @param string msg - the message to send
607
* @return int 1 if successful
609
private int send_subgroup(object group, object parent, string msg, string envFrom)
611
mapping headers = ([]);
612
headers["X-sTeam-Subgroup"] = group->get_identifier();
613
msg = (((array)headers)[*]*": ")*"\r\n" + "\r\n" + msg;
615
//TODO: only send to users that want a copy of group mails
616
array(string) members = group->get_members(CLASS_USER)->get_user_name();
617
send_message_raw(members, msg, envFrom);
619
array subgroups = group->get_sub_groups();
620
foreach(subgroups;; object subgroup)
621
send_subgroup(subgroup, parent, msg, envFrom);
313
626
* send a simple message (subject & text) to multiple recipients
315
628
* @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
448
766
* @param string|void envFrom - the sender-value of the SMTP-envelope
449
767
* @return int 1 if successful, 0 if not
451
private int send_remote(string address, string rawText, string envFrom)
769
private int send_remote(string|array address, string rawText, string envFrom)
772
if( arrayp(address) && sizeof(address)==0 ) return 1;
773
if( stringp(address) && strlen(address) == 0 ) return 1;
454
775
if( sscanf(envFrom,"%*s<%s>",fixed) == 0 )
456
777
LOG_FORWARD("send_remote: illegal envFrom! : "+envFrom);
459
get_module("smtp")->send_mail_raw(address, rawText, fixed);
460
LOG_FORWARD("Message delivered to " + address);
781
if ( arrayp(address) && (l=sizeof(address)) > 10 ) {
782
for ( int i = 0; i < sizeof(address); i+=10 ) {
785
users = address[i..];
787
users = address[i..i+9];
788
get_module("smtp")->send_mail_raw(users, rawText, fixed);
789
LOG_FORWARD("Message chunked delivered to "+sprintf("%O", users));
793
get_module("smtp")->send_mail_raw(address, rawText, fixed);
794
LOG_FORWARD("Message delivered to " + sprintf("%O", address));
465
* add an alias to system-aliases
466
* if an alias with the given name already exists, the alias
467
* will point to a list of targets; if you want to replace an alias
468
* completely, you'll have to delete it first
801
* Adds an alias to the system-aliases.
802
* If an alias with the given name already exists, the alias
803
* will point to a list of targets. The target will be added to the
804
* alias if it hasn't been added before. If you want to replace an alias
805
* completely, you'll have to delete it first.
470
807
* @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
471
808
* @param string alias - the name of the alias to add
538
902
* @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
539
903
* @param object user - the user to remove forwarding for
904
* @param forward - the forward to remove (if not specified, all forwards
905
* for that user will be removed)
540
906
* @return int 1 if successful, 0 otherwise
542
int delete_forward(object user)
908
int delete_forward(object user, void|string forward)
544
if(user->get_object_class() && CLASS_USER)
546
if(arrayp(mForwards[user->get_identifier()]))
548
m_delete(mForwards,user->get_identifier());
552
else return 0; //no forward for this user
910
if ( ! objectp(user) || !(user->get_object_class() && CLASS_USER) ) {
911
LOG_FORWARD("delete_forward: invalid user %O", user);
915
string name = user->get_identifier();
916
if ( ! arrayp(mForwards[name] ) ) {
917
LOG_FORWARD("delete_forward: user %s has no forwards", name);
918
return 0; // no forwards for this user
921
if ( zero_type(forward) ) { // delete all forwards for user
922
LOG_FORWARD("deleting forwards for %s", name);
923
m_delete(mForwards,name);
925
else { // delete single forward
926
LOG_FORWARD("deleting forward %O for %s", forward, name);
927
if ( search( mForwards[name], forward ) < 0 ) {
928
LOG_FORWARD("forward %O not found for %s", forward, name);
554
else return 0; //user is no sTeam-user-object
931
mForwards[name] -= ({ forward });