forward._pike
Go to the documentation of this file.
1 /* Copyright (C) 2004 Christian Schmidt
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17 inherit "/kernel/module";
18 #include <database.h>
19 #include <macros.h>
20 #include <classes.h>
21 #include <config.h>
22 #include <attributes.h>
23 #include <events.h>
24 class forward : public module{
25 public:
26 
27 
28 /**
29  * This module is responsible for sending messages to users/groups/... and
30  * e-mails to remote adresses. It also keeps track of system-wide
31  * alias-adresses and of user-forwards.
32  */
33 
34 
35 
36 //#define DEBUG_FORWARD
37 
38 #ifdef DEBUG_FORWARD
39 #define LOG_FORWARD(s, args...) werror("forward: "+s+"\n", args)
40 #else
41 #define LOG_FORWARD
42 #endif
43 
44 #if constant(Protocols.SMTP.client)
45 #define SMTPCLIENT Protocols.SMTP.client
46 #else
47 #define SMTPCLIENT Protocols.SMTP.Client
48 #endif
49 
50 //stores aliases & forwards
51  mapping(string:array) mAliases, mForwards;
52  mapping(object:object) mListeners;
53  array whiteDomains;
54 
55 string _mailserver;
56 int _mailport;
57 
58 string get_mask_char() { return "/";}
59 
60 private:
61 void init_module()
62 {
63  mAliases=([]);
64  mForwards=([]);
65  mListeners=([]);
66  whiteDomains = Config.array_value(_Server->get_config("mail_whitedomains")) || ({ });
67  add_data_storage(STORE_FORWARD,retrieve_aliases,restore_aliases);
68 }
69 
70 public:
71 
72 protected:
73  array|void get_parent_groups(object group)
74 {
75  object parent = group->get_parent();
76  array parents = ({});
77  if(!objectp(parent))
78  return;
79  else
80  parents += ({ parent });
81  array grandparents=get_parent_groups(parent);
82  if(arrayp(grandparents) )
83  parents += grandparents;
84  return parents;
85 }
86 
87 public:
88 
89 protected:
90  array get_user_groups(object user)
91 {
92  array groups;
93  array usergroups = groups = user->get_groups();
94  foreach(usergroups;; object group)
95  {
96  array parents = get_parent_groups(group);
97  if(arrayp(parents))
98  groups += parents;
99  }
100  return(Array.uniq(groups));
101 }
102 
103 public:
104 
105 void load_module()
106 {
107  array groups = ({});
108  LOG_FORWARD("forwards are: %O\n", mForwards);
109 
110  low_add_alias("abuse", "admin");
111 }
112 
113 protected:
114  void send_annotation_remote(int event, object group, object annotated,
115  object caller, object ... thread)
116 {
117  // find root of thread...
118  object parent = thread[-1];
119  while ( objectp(parent->get_annotating()) )
120  {
121  parent = parent->get_annotating();
122  }
123 
124  LOG_FORWARD("send_annotation_remote(event: %d, group: %O, annotated: %O, caller: %O, thread: %s) - parent: %O\n",
125  event, group->get_identifier(), annotated->get_identifier(),
126  caller->get_identifier(), thread->get_identifier()*", ",
127  parent->get_identifier());
128 
129  if(parent==annotated)
130  {
131  LOG_FORWARD("sending to %s", group->get_identifier());
132  send_group(group, thread[-1]);
133  }
134 }
135 
136 public:
137 
138 void install_module()
139 {
140  _mailserver = _Server->query_config(CFG_MAILSERVER);
141  _mailport = (int)_Server->query_config(CFG_MAILPORT);
142  LOG_FORWARD("mailserver is: "+_mailserver+":"+_mailport);
143 }
144 
145 string get_identifier() { return "forward"; }
146 
147 mapping get_aliases()
148 {
149  return mAliases;
150 }
151 
152 array get_alias ( string key ) {
153  if ( arrayp(mAliases[key]) )
154  return copy_value( mAliases[key] );
155  else
156  return UNDEFINED;
157 }
158 
159 private:
160 mapping retrieve_aliases()
161 {
162  if ( CALLER != _Database )
163  THROW("Caller is not database !", E_ACCESS);
164 
165  return (["aliases" : mAliases, "forwards" : mForwards]);
166 }
167 
168 public:
169 
170 private:
171 void restore_aliases(mapping data)
172 {
173  if ( CALLER != _Database )
174  THROW("Caller is not database !", E_ACCESS);
175 
176  mAliases=data["aliases"];
177  mForwards=data["forwards"];
178 
179  LOG_FORWARD("loaded "+sizeof(mAliases)+" aliases and "
180  +sizeof(mForwards)+" forwards");
181 }
182 
183 public:
184 
185 /**
186  * check if an address is valid
187  *
188  * @param string address - the address to check
189  * @return int 1 if valid, 0 if invalid, -1 if access denied
190  */
191 int is_valid(string address)
192 {
193  LOG_FORWARD("checking adress \"%O\"",address);
194  LOG_FORWARD("alias...");
195  if(arrayp(mAliases[address]))
196  return 1; //adress is alias
197  LOG_FORWARD("no - trying user...");
198  if(objectp(MODULE_USERS->lookup(address)))
199  return 1; //address is single user
200  LOG_FORWARD("no - trying group...");
201  if ( address == "steam" )
202  return 0; // no mailing to steam-group allowed
203  if(objectp(MODULE_GROUPS->lookup(address)))
204  return 1; //adress is a group
205  LOG_FORWARD("no - trying to replace - with space...");
206  if(objectp(MODULE_GROUPS->lookup(replace(address, "-", " "))))
207  return 1; //adress is a group
208  LOG_FORWARD("no - trying object-id...");
209  if(sscanf(address,"%d",int oid))
210  {
211  LOG_FORWARD("looking for object #%O",oid);
212  object tmp=_Database->find_object(oid);
213  if(objectp(tmp))
214  {
215  LOG_FORWARD("checking access on object #%O",oid);
216  mixed err = catch { _SECURITY->access_annotate(0, tmp, CALLER, 0); };
217  if(err!=0) return -1; //access denied -> invalid target-address
218  else return 1; //target is existing object & annotatable
219  }
220  else LOG_FORWARD("not found");
221  }
222  // TODO: check for paths (replace - with /)
223  //IDEA: allow creation of subgroups and forum objects here
224  // split user part into components . for subgroups, - for areas
225  //LOG_FORWARD("no - trying to replace - with /...");
226  //
227  LOG_FORWARD("sorry - no valid target found!");
228 
229  return 0; //no checks succeeded, target address is invalid
230 }
231 
232 /**
233  * return the remote addresses within an array of mixed addresses
234  *
235  * @param array names - the addresses to get remotes from
236  * @return array the remote adresses
237  */
238 private:
239 private array get_remote_addresses(array names)
240 {
241  array result=({});
242  for(int i=0;i<sizeof(names);i++)
243  if(search(names[i],"@")!=-1)
244  result+=({names[i]});
245  return result;
246 }
247 
248 public:
249 
250 string resolve_name(object grp)
251 {
252  if ( objectp(grp) ) {
253  if ( grp->get_object_class() & CLASS_USER )
254  return grp->get_user_name();
255  return grp->get_identifier();
256  }
257  return "";
258 }
259 
260 /**
261  * split targets into remote, groups, users, and objects
262  *
263  * @param array names - the addresses to work with
264  * @return mapping - containing the different recipient types
265  */
266 private:
267 private mapping resolve_recipients(array names)
268 {
269  array unresolved=({});
270  LOG_FORWARD("Resolving: %O", names);
271  array resolved=replace_aliases(names);
272  LOG_FORWARD("Aliases replaced: %O", resolved);
273  resolved=replace_forwards(resolved);
274  LOG_FORWARD("Forwards are: %O", resolved);
275 
276  mapping result =([ "groups":({}), "remote":({}), "users":({}),
277  "objects":({}) ]);
278  object target_obj;
279  int oid;
280 
281  foreach(resolved;; string target)
282  {
283  if(search(target,"@")!=-1)
284  result->remote += ({ target });
285  else if ( objectp(target_obj=MODULE_GROUPS->lookup(target)) )
286  result->groups += ({ target_obj });
287  // groupnames may have spaces, but those don't work well with email.
288  else if ( objectp(target_obj=MODULE_GROUPS->lookup(replace(target, "-", " "))) )
289  result->groups += ({ target_obj });
290  else if ( objectp(target_obj=MODULE_USERS->lookup(target)) )
291  result->users += ({ target_obj });
292  else if ( sscanf(target,"%d",oid)
293  && objectp(target_obj=_Database->find_object(oid)) )
294  result->objects += ({ target_obj });
295  else
296  unresolved += ({ target });
297  }
298 
299  LOG_FORWARD("Remote adresses are: %O", result->remote);
300  LOG_FORWARD("Group addresses are: %O", result->groups);
301  LOG_FORWARD("User addresses are: %O", result->users);
302  LOG_FORWARD("Object addresses are: %O", result->objects);
303  if(sizeof(unresolved))
304  FATAL("Warning! unresolved addresses: %O", unresolved);
305  return result;
306 }
307 
308 public:
309 
310 /**
311  * within an array of adresses, search for aliases and replace them with
312  * their targets
313  *
314  * @param array names - the addresses to work with
315  * @return array the input-array with aliases replaced by targets
316  */
317 private:
318 private array replace_aliases(array names)
319 {
320  array result=({});
321  foreach(names;; string name)
322  {
323  if(arrayp(mAliases[name])) //add code for checking aliases on aliases here...
324  result+=mAliases[name];
325  else
326  result+=({name});
327  }
328  return Array.uniq(result);
329 }
330 
331 public:
332 
333 
334 /**
335  * within an array of adresses, search for forwards and replace them with
336  * their targets
337  *
338  * @param array names - the addresses to work with
339  * @return array the input-array with forwards replaced by targets
340  */
341 private:
342 private array replace_forwards(array names, void|mapping fwd_done)
343 {
344  array result=({});
345 
346  if ( !mappingp(fwd_done) )
347  fwd_done = ([ ]);
348 
349  for(int i=0;i<sizeof(names);i++)
350  {
351  if ( fwd_done[names[i]] )
352  continue;
353  if(arrayp(mForwards[names[i]]))
354  {
355  array tmp=mForwards[names[i]];
356  for(int j=0;j<sizeof(tmp);j++)
357  {
358  if ( !stringp(tmp[j]) )
359  continue;
360 
361  fwd_done[tmp[j]] = 1;
362  if(search(tmp[j],"@")!=-1) //remote address
363  result+=({tmp[j]});
364  else
365  {
366  if(search(tmp[j],"/")!=-1) //local forward-target starts with "/" -> don't forward further
367  result+=({tmp[j]-"/"});
368  else //lookup forward of this forward-target
369  {
370  array tmp2=replace_aliases( ({tmp[j]}) );
371  result+=replace_forwards(tmp2, fwd_done);
372  }
373  }
374  }
375  }
376  else result+=({names[i]});
377  }
378  return result;
379 }
380 
381 public:
382 
383 /**
384  * send a message to multiple recipients
385  *
386  * @param array target - the addresses to send the message to
387  * @param Message msg - the message to send (WARNING msg is destructed by sending!)
388  * @return int 1 if successful
389  */
390 int send_message(array target, Messaging.Message msg)
391 {
392  int hasLocal=0;
393  string rawText=msg->complete_text();
394  string sSender=msg->sender();
395  array resolved=replace_aliases(target);
396  resolved=replace_forwards(resolved);
397  array asRemote=get_remote_addresses(resolved);
398 
399  array asLocal=resolved-asRemote;
400  if(sizeof(asLocal)>0) hasLocal=1;
401  if(hasLocal)
402  send_local(asLocal,msg);
403  else
404  destruct(msg);
405 
406  LOG_FORWARD("Sending to " + sizeof(asRemote) + " Remote Recipients !");
407 
408  asRemote = Array.uniq(asRemote);
409  send_remote(asRemote, rawText, sSender);
410  return 1;
411  for(int i=0;i<sizeof(asRemote);i++)
412  send_remote(asRemote[i],rawText,sSender);
413 
414  return 1; //success, add code for failures!
415 }
416 
417 object lookup_sender(string sender)
418 {
419  string user, domain;
420 
421  sscanf(sender, "%*s<%s>", sender);
422  sscanf(sender, "%s@%s", user, domain);
423  if (strlen(sender)==0)
424  return USER("postman"); // as in RFC821 empty reverse path is allowed
425 
426  array users = get_module("users")->lookup_email(sender);
427  if (sizeof(users)==0) {
428  foreach(whiteDomains, string d) {
429  if (search(domain, d) >= 0)
430  return USER("postman");
431  }
432  }
433  if (sizeof(users) > 0)
434  return users[0];
435  return 0;
436 }
437 
438 
439 /**
440  * send a message (rfc2822 raw text) to multiple recipients
441  *
442  * @param array target - the addresses to send the message to
443  * @param string rawText - the text of the message (rfc2822-format!)
444  * @param string|void envFrom - the sender-value of the SMTP-envelope (only needed for forwarding, may be left empty)
445  * @return int 1 if successful
446  */
447 int send_message_raw(array target,
448  string rawText,
449  string envFrom,
450  void|object messageObj)
451 {
452  int res=1; //success
453 
454  float tt = gauge {
455  mixed err;
456  LOG_FORWARD("Forward: send_message_raw(%O)", target);
457  mapping sendAs = resolve_recipients(target);
458  if(sizeof(sendAs->users)) {
459  if (!objectp(messageObj)) {
460  messageObj = Messaging.MIME2Message(rawText);
461  }
462  err = catch(res=send_users(sendAs->users, messageObj));
463  }
464  if ( err )
465  FATAL("Error while sending message to users: %O, %O", err[0], err[1]);
466  if (res==0)
467  LOG_FORWARD("Warning! send_message_raw failed on one ore more recipient users!");
468  res=1;
469 
470  if(sizeof(sendAs->objects)) {
471  if (!objectp(messageObj))
472  messageObj = Messaging.MIME2Message(rawText);
473  err = catch(res=send_objects(sendAs->objects, messageObj));
474  }
475  if ( err )
476  FATAL("Error while sending message to objects: %O,%O", err[0], err[1]);
477 
478  if (res==0)
479  LOG_FORWARD("Warning! send_message_raw failed on one ore more recipient objects!");
480 
481  send_remote(sendAs->remote, rawText, envFrom);
482  foreach(sendAs->groups;; object target)
483  send_group(target,rawText,envFrom,messageObj);
484  };
485  MESSAGE("Message to %s send in %f seconds", (target*","), tt);
486 
487  return res;
488 }
489 
490 /**
491  * send a message to objects
492  *
493  * @param array targets - the objects to send the message to
494  * @param string msg - the message to send
495  * @return int 1 if successful
496  */
497 private:
498 private int send_objects(array targets, Messaging.Message msg)
499 {
500  LOG_FORWARD("send_objects(%O, %O)\n", targets, msg->header()->subject);
501  if(sizeof(targets)==0 || !arrayp(targets)) return 0;
502 
503  int errors;
504 
505  seteuid(USER("root"));
506  foreach(targets; int count; object target)
507  {
508  Messaging.Message copy;
509  if(count<sizeof(targets)-1)
510  // duplicate message if more than one recipient
511  copy=msg->duplicate();
512  else
513  // last recipient gets original
514  copy=msg;
515 
516  mixed err=catch{Messaging.add_message_to_object(copy,target);};
517  if(err)
518  {
519  copy->delete();
520  destruct(copy);
521  errors++;
522  FATAL("unable to add message to: %s(%d):%O", target->get_identifier(), target->get_object_id(), err);
523  }
524  }
525 
526  if(errors)
527  {
528  LOG_FORWARD("Warning!! send_objects encountered errors - some recipients failed!");
529  return 0;
530  }
531 
532  return 1;
533 }
534 
535 public:
536 
537 /**
538  * send a message to users
539  *
540  * @param array users - the users to send the message to
541  * @param string msg - the message to send
542  * @return int 1 if successful
543  */
544 private:
545 private int send_users(array targets, Messaging.Message msg)
546 {
547  if(sizeof(targets)==0 || !arrayp(targets)) return 0;
548 
549  foreach(targets; int count; object user)
550  {
551  if ( !objectp(user) )
552  continue;
553  Messaging.Message copy;
554  if(count<sizeof(targets)-1)
555  copy=msg->duplicate();
556  else
557  copy=msg; // last recipient gets original
558 
559  //the recipient gets all rights on his/her copy
560  copy->grant_access(user);
561 
562  Messaging.BaseMailBox box = Messaging.get_mailbox(user);
563  box->add_message(copy);
564  }
565 
566  seteuid(0);
567  return 1;
568 }
569 
570 public:
571 
572 /**
573  * create headers to be added to annotations sent as mails
574  *
575  * @param object group - the group to send the message to
576  * @return mapping of headers
577  */
578 mapping create_list_headers(object group)
579 {
580  mapping headers = ([]);
581  headers["X-Steam-Group"] = group->get_identifier();
582  headers["List-Id"] = replace(group->get_identifier(), " ", "-")+"@"+
583  (_Server->query_config("smtp_host")||(_Server->query_config("machine")+"."+_Server->query_config("domain")));
584 
585  object group_workroom = group->query_attribute("GROUP_WORKROOM");
586  object modpath=get_module("filepath:tree");
587  headers["X-Steam-Path"] = _Server->query_config("web_server")+
588  modpath->object_to_filename(group_workroom);
589 
590  headers["X-Steam-Annotates"] = _Server->query_config("web_server")+":"
591  +(string)group_workroom->get_object_id();
592  return headers;
593 }
594 
595 /**
596  * send a message to a group
597  *
598  * @param object group - the group to send the message to
599  * @param string|object msg - the message to send
600  * @return int 1 if successful
601  */
602 private:
603 private int send_group(object group,
604  string|object msg,
605  string|void envFrom,
606  void|object messageobj)
607 {
608  LOG_FORWARD("send_group(%O)\n", group);
609  mapping headers = create_list_headers(group);
610  string rawmessage;
611 
612  if(stringp(msg))
613  {
614  rawmessage = (((array)headers)[*]*": ")*"\r\n" + "\r\n" + msg;
615  if (!objectp(messageobj))
616  messageobj = Messaging.MIME2Message(rawmessage);
617 
618  //string messages come from outside, need to be added to group
619  send_objects( ({ group->query_attribute(GROUP_WORKROOM) }), messageobj);
620  }
621  else if(objectp(msg))
622  {
623  messageobj = Messaging.Message(msg);
624  if(!envFrom)
625  envFrom = "<"+messageobj->sender()+">";
626  LOG_FORWARD("sender is: %O\n", messageobj->sender());
627  messageobj->add_to_header(headers);
628  messageobj->add_to_header(([ "to":headers["List-Id"] ]));
629  rawmessage = (string)messageobj->mime_message();
630  }
631  else
632  {
633  FATAL("Warning! unknown message format: %O", msg);
634  return 0;
635  }
636 
637  string settings = group->query_attribute(GROUP_MAIL_SETTINGS) || "open";
638  if ( objectp(get_module("users")) && settings != "open" ) {
639  string sender = messageobj->sender() || "";
640  sscanf(sender, "%*s <%s>", sender);
641  array fromUsers = get_module("users")->lookup_email(sender);
642  object fromUser;
643  if ( sizeof(fromUsers) > 0 )
644  fromUser = fromUsers[0];
645  if ( !objectp(fromUser) ) {
646  FATAL("Unknown Sending User %O - not relayed", sender);
647  return 0;
648  }
649  if ( settings == "closed" && !group->is_member(fromUser) ) {
650  FATAL("User %O is not a member of group %O - not relayed",
651  fromUser, group);
652  return 0;
653  }
654  }
655 
656  //TODO: only send to users that want a copy of group mails
657  array members = group->get_members_recursive(CLASS_USER)->get_user_name();
658  // each member gets mail once
659  members = Array.uniq(members);
660  send_message_raw(members, rawmessage, envFrom, messageobj);
661 
662  // store message on group
663  mixed err = catch {
664  group->add_annotation(messageobj->get_msg_object()->duplicate());
665  };
666  if ( err ) {
667  FATAL("Failed to store annotation on group: %O\n%O", err[0], err[1]);
668  }
669 
670 
671  return 1;
672 }
673 
674 public:
675 
676 /**
677  * send a message to a subgroup
678  *
679  * @param object group - the group to send the message to
680  * @param object parent - the group that the message was initially sent to
681  * @param string msg - the message to send
682  * @return int 1 if successful
683  */
684 private:
685 private int send_subgroup(object group, object parent, string msg, string envFrom)
686 {
687  mapping headers = ([]);
688  headers["X-sTeam-Subgroup"] = group->get_identifier();
689  msg = (((array)headers)[*]*": ")*"\r\n" + "\r\n" + msg;
690 
691  //TODO: only send to users that want a copy of group mails
692  array members = group->get_members(CLASS_USER)->get_user_name();
693  send_message_raw(members, msg, envFrom);
694 
695  array subgroups = group->get_sub_groups();
696  foreach(subgroups;; object subgroup)
697  send_subgroup(subgroup, parent, msg, envFrom);
698 }
699 
700 public:
701 
702 
703 /**
704  * send a simple message (subject & text) to multiple recipients
705  *
706  * @param array target - the addresses to send the message to
707  * @param string subject - the subject of the message to send
708  * @param string message - the text of the message to send
709  * @return int 1 if successful
710  */
711 int send_message_simple(array target, string subject, string message)
712 {
713  int hasLocal=0;
714  Messaging.Message msg = Messaging.SimpleMessage(target, subject, message);
715  string rawText=msg->complete_text();
716  string sSender=msg->sender();
717  array resolved=replace_aliases(target);
718  resolved=replace_forwards(resolved);
719  array asRemote=get_remote_addresses(resolved);
720  array asLocal=resolved-asRemote;
721  if(sizeof(asLocal)>0) hasLocal=1;
722  if(hasLocal)
723  send_local(asLocal,msg);
724  else destruct(msg);
725 
726  asRemote = Array.uniq(asRemote);
727  send_remote(asRemote, rawText, sSender);
728  return 1;
729  for(int i=0;i<sizeof(asRemote);i++)
730  send_remote(asRemote[i],rawText,sSender);
731 
732  return 1; //success, add code for failures!
733 }
734 
735 /**
736  * replace group-entries with members of group
737  * object ids included in input are not changed by this
738  *
739  * @param array target - the addresses to replace groups in
740  * @return array input-array with groups replaced by members of groups
741  */
742 private:
743 private array expand_local_addresses(array target)
744 {
745  array result=({});
746  for(int i=0;i<sizeof(target);i++)
747  {
748  if(objectp(MODULE_USERS->lookup(target[i])))
749  {
750  result+=({target[i]});
751  continue;
752  }
753  // FIXME: this is dead code! all groups should have been removed by now.
754  object tmp=MODULE_GROUPS->lookup(target[i]);
755  if(objectp(tmp))
756  {
757  result+=tmp->get_members();
758  continue;
759  }
760  if(sscanf(target[i],"%d",int oid))
761  {
762  result+=({target[i]});
763  continue;
764  }
765  LOG_FORWARD("expand_local_addresses: failed to find \""+target[i]+"\"");
766  }
767  return result; //now contains only user-names and object-ids
768 }
769 
770 public:
771 
772 private:
773 private int send_local_single(string recipient, Messaging.Message msg)
774 {
775  int oid;
776  if(sscanf(recipient,"%d",oid)!=1)
777  {
778  object user=MODULE_USERS->lookup(recipient);
779  Messaging.BaseMailBox box = Messaging.get_mailbox(user);
780  msg->grant_access(user); //the recipient gets all rights on his/her copy
781  box->add_message(msg);
782  }
783  else //store message on object
784  {
785  object target=_Database->find_object(oid);
786  if(!objectp(target)) return 0; //invalid object-id, do nothing
787 // msg->grant_access(target->query_attribute(OBJ_OWNER));
788  Messaging.BaseMailBox box = Messaging.get_mailbox(target);
789  if(objectp(box)) //target can be accessed as mailbox
790  box->add_message(msg);
791  else
792  Messaging.add_message_to_object(msg,target);
793  }
794  return 1;
795 }
796 
797 public:
798 /**
799  * send a message to local recipients
800  *
801  * @param array target - the local addresses to send the message to
802  * @param Message msg - the message to send (WARNING msg is destructed by sending!)
803  * @return int 1 if successful
804  */
805 private:
806 private int send_local(array target, Messaging.Message msg)
807 {
808  if(sizeof(target)==0 || !arrayp(target)) return 0;
809 
810  int result=1; //success
811 
812  // FIXME: expand_local_addresses should not be needed anymore:
813  array asLocal=expand_local_addresses(target); //resolve aliases & forwards
814  LOG_FORWARD("expanded local adresses are:%O",asLocal);
815 
816  for(int i=sizeof(asLocal)-1;i>0;i--) //duplicate message if more than one recipient
817  {
818  Messaging.Message copy=msg->duplicate();
819  if(send_local_single(asLocal[i],copy)==0)
820  {
821  LOG_FORWARD("failed to send message #"+copy->get_object_id()+" to: "+asLocal[i]);
822  copy->delete();
823  destruct(copy);
824  result=0;
825  }
826  }
827  if(send_local_single(asLocal[0],msg)==0) //last recipient gets "original" message
828  {
829  LOG_FORWARD("failed to send message #"+msg->get_object_id()+" to: "+asLocal[0]);
830  msg->delete();
831  destruct(msg);
832  result=0;
833  }
834 
835  if(result==0) LOG_FORWARD("Warning!! send_local encountered errors - some recipients failed!");
836 
837  return result;
838 }
839 
840 public:
841 
842 /**
843  * send a message to a remote address (-> send e-mail)
844  *
845  * @param string address - the address to send the mail to
846  * @param string rawText - the text of the message (rfc2822-format!)
847  * @param string|void envFrom - the sender-value of the SMTP-envelope
848  * @return int 1 if successful, 0 if not
849  */
850 private:
851 private int send_remote(string|array address, string rawText, string envFrom)
852 {
853  string fixed;
854  if( arrayp(address) && sizeof(address)==0 ) return 1;
855  if( stringp(address) && strlen(address) == 0 ) return 1;
856 
857  if( sscanf(envFrom,"%*s<%s>",fixed) == 0 )
858  {
859  if ( search( envFrom, '@' ) >= 0 )
860  fixed = envFrom;
861  else {
862  FATAL("send_remote: illegal envFrom! : "+envFrom);
863  return 0;
864  }
865  }
866  int l;
867  if ( arrayp(address) && (l=sizeof(address)) > 10 ) {
868  for ( int i = 0; i < sizeof(address); i+=10 ) {
869  array users;
870  if ( i + 10 >= l )
871  users = address[i..];
872  else
873  users = address[i..i+9];
874  get_module("smtp")->send_mail_raw(users, rawText, fixed);
875  LOG_FORWARD("Message chunked delivered to "+sprintf("%O", users));
876  }
877  }
878  else {
879  get_module("smtp")->send_mail_raw(address, rawText, fixed);
880  LOG_FORWARD("Message delivered to " + sprintf("%O", address));
881  }
882 
883  return 1;
884 }
885 
886 public:
887 
888 /**
889  * Adds an alias to the system-aliases.
890  * If an alias with the given name already exists, the alias
891  * will point to a list of targets. The target will be added to the
892  * alias if it hasn't been added before. If you want to replace an alias
893  * completely, you'll have to delete it first.
894  *
895  * @param string alias - the name of the alias to add
896  * @param string taget - the target the alias should point to
897  * @return 1 if successful
898  * @see delete_alias
899  */
900 protected:
901  int low_add_alias(string alias, string target)
902 {
903  if ( (arrayp(mAliases[alias]) && search(mAliases[alias], target)>=0) )
904  return 1; // target was already set for that alias
905 
906  if ( !arrayp(mAliases[alias]) )
907  mAliases[alias] = ({ });
908 
909  mAliases[alias]+=({target});
910  return 1;
911 }
912 
913 public:
914 
915 int add_alias(string alias, string target)
916 {
917  _SECURITY->access_write(0, this_object(), CALLER);
918  return low_add_alias(alias, target);
919 }
920 
921 /**
922  * remove an alias from the system aliases
923  *
924  * @param string alias - the alias to delete
925  * @return 1 if successful, 0 if alias does not exist
926  * @see add_alias
927  */
928 protected:
929  int low_delete_alias(string alias)
930 {
931  if(arrayp(mAliases[alias]))
932  {
933  m_delete(mAliases,alias);
934  return 1;
935  }
936  else return 0;
937 }
938 
939 public:
940 
941 int delete_alias(string alias)
942 {
943  _SECURITY->access_write(0, this_object(), CALLER);
944  return low_delete_alias(alias);
945 }
946 
947 /**
948  * add a forward for a specific user
949  * if user already has a forward, it is extended by the given target
950  * target may be a remote e-mail address or a local system address
951  * (user, group, object-id, alias)
952  *
953  * @param object user - the user to add a forward for
954  * @param string forward - the target address to add (e-mail or other valid system-address)
955  * @param int|void no_more - set to 1, if this forward is "final", so mails get stored at this adress,
956  * no matter if a forward exists for this target, too
957  * @return int 1 if successful, 0 if not
958  */
959 int add_forward(object user, string forward, int|void no_more)
960 {
961  _SECURITY->access_write(0, user, CALLER);
962 
963  LOG_FORWARD("adding forward for %s:%s", user->get_user_name(), forward);
964  if ( !stringp(forward) ) {
965  FATAL("invalid forward: %O", forward);
966  return 0;
967  }
968 
969  if(intp(no_more) && no_more==1) forward="/"+forward;
970  if(user->get_object_class() & CLASS_USER)
971  {
972  string name=user->get_identifier();
973  if ( forward == name )
974  steam_error("add_forward: Unable to resolve forward to itself !");
975 
976  if ( mForwards[name] && search( mForwards[name], forward ) >= 0 ) {
977  LOG_FORWARD("add_forward(%s : %s) : forward already exists",
978  name, forward);
979  return 0; // forward already exists
980  }
981  mForwards[name]+=({forward});
982  return 1;
983  }
984  else
985  {
986  LOG_FORWARD("ERROR, add_forward() called for non-user object #"
987  +user->get_object_id());
988  return 0;
989  }
990 }
991 
992 /**
993  * remove a user-forward
994  *
995  * @param object user - the user to remove forwarding for
996  * @param forward - the forward to remove (if not specified, all forwards
997  * for that user will be removed)
998  * @return int 1 if successful, 0 otherwise
999  */
1000 int delete_forward(object user, void|string forward)
1001 {
1002  _SECURITY->access_write(0, user, CALLER);
1003 
1004  if ( ! objectp(user) || !(user->get_object_class() && CLASS_USER) ) {
1005  LOG_FORWARD("delete_forward: invalid user %O", user);
1006  return 0;
1007  }
1008 
1009  string name = user->get_identifier();
1010  if ( ! arrayp(mForwards[name] ) ) {
1011  LOG_FORWARD("delete_forward: user %s has no forwards", name);
1012  return 0; // no forwards for this user
1013  }
1014 
1015  if ( zero_type(forward) ) { // delete all forwards for user
1016  LOG_FORWARD("deleting forwards for %s", name);
1017  m_delete(mForwards,name);
1018  }
1019  else { // delete single forward
1020  LOG_FORWARD("deleting forward %O for %s", forward, name);
1021  if ( search( mForwards[name], forward ) < 0 ) {
1022  LOG_FORWARD("forward %O not found for %s", forward, name);
1023  return 0;
1024  }
1025  mForwards[name] -= ({ forward });
1026  }
1027  return 1;
1028 }
1029 
1030 /**
1031  * get the current forward for a user
1032  *
1033  * @param object user - the user to get forward for
1034  * @return array of forwards or 0 if user is not a sTeam-user-object
1035  */
1036 array get_forward(object user)
1037 {
1038  if(user->get_object_class() & CLASS_USER)
1039  return mForwards[user->get_identifier()];
1040  else return 0;
1041 }
1042 
1043 /*
1044 string dump_data()
1045 {
1046  string res;
1047  res=sprintf("forwards:%O aliases:%O",mForwards,mAliases);
1048  LOG_FORWARD("current data of forward-module:\n"+res);
1049  LOG_FORWARD("mailserver is: "+_mailserver+":"+_mailport);
1050  return res;
1051 }
1052 */
1053 
1054 
1055 };