1 /* Copyright (C) 2004 Christian Schmidt
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.
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.
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
17 inherit "/kernel/module";
22 #include <attributes.h>
24 class forward : public module{
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.
36 //#define DEBUG_FORWARD
39 #define LOG_FORWARD(s, args...) werror("forward: "+s+"\n", args)
44 #if constant(Protocols.SMTP.client)
45 #define SMTPCLIENT Protocols.SMTP.client
47 #define SMTPCLIENT Protocols.SMTP.Client
50 //stores aliases & forwards
51 mapping(string:array) mAliases, mForwards;
52 mapping(object:object) mListeners;
58 string get_mask_char() { return "/";}
66 whiteDomains = Config.array_value(_Server->get_config("mail_whitedomains")) || ({ });
67 add_data_storage(STORE_FORWARD,retrieve_aliases,restore_aliases);
73 array|void get_parent_groups(object group)
75 object parent = group->get_parent();
80 parents += ({ parent });
81 array grandparents=get_parent_groups(parent);
82 if(arrayp(grandparents) )
83 parents += grandparents;
90 array get_user_groups(object user)
93 array usergroups = groups = user->get_groups();
94 foreach(usergroups;; object group)
96 array parents = get_parent_groups(group);
100 return(Array.uniq(groups));
108 LOG_FORWARD("forwards are: %O\n", mForwards);
110 low_add_alias("abuse", "admin");
114 void send_annotation_remote(int event, object group, object annotated,
115 object caller, object ... thread)
117 // find root of thread...
118 object parent = thread[-1];
119 while ( objectp(parent->get_annotating()) )
121 parent = parent->get_annotating();
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());
129 if(parent==annotated)
131 LOG_FORWARD("sending to %s", group->get_identifier());
132 send_group(group, thread[-1]);
138 void install_module()
140 _mailserver = _Server->query_config(CFG_MAILSERVER);
141 _mailport = (int)_Server->query_config(CFG_MAILPORT);
142 LOG_FORWARD("mailserver is: "+_mailserver+":"+_mailport);
145 string get_identifier() { return "forward"; }
147 mapping get_aliases()
152 array get_alias ( string key ) {
153 if ( arrayp(mAliases[key]) )
154 return copy_value( mAliases[key] );
160 mapping retrieve_aliases()
162 if ( CALLER != _Database )
163 THROW("Caller is not database !", E_ACCESS);
165 return (["aliases" : mAliases, "forwards" : mForwards]);
171 void restore_aliases(mapping data)
173 if ( CALLER != _Database )
174 THROW("Caller is not database !", E_ACCESS);
176 mAliases=data["aliases"];
177 mForwards=data["forwards"];
179 LOG_FORWARD("loaded "+sizeof(mAliases)+" aliases and "
180 +sizeof(mForwards)+" forwards");
186 * check if an address is valid
188 * @param string address - the address to check
189 * @return int 1 if valid, 0 if invalid, -1 if access denied
191 int is_valid(string address)
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))
211 LOG_FORWARD("looking for object #%O",oid);
212 object tmp=_Database->find_object(oid);
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
220 else LOG_FORWARD("not found");
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 /...");
227 LOG_FORWARD("sorry - no valid target found!");
229 return 0; //no checks succeeded, target address is invalid
233 * return the remote addresses within an array of mixed addresses
235 * @param array names - the addresses to get remotes from
236 * @return array the remote adresses
239 private array get_remote_addresses(array names)
242 for(int i=0;i<sizeof(names);i++)
243 if(search(names[i],"@")!=-1)
244 result+=({names[i]});
250 string resolve_name(object grp)
252 if ( objectp(grp) ) {
253 if ( grp->get_object_class() & CLASS_USER )
254 return grp->get_user_name();
255 return grp->get_identifier();
261 * split targets into remote, groups, users, and objects
263 * @param array names - the addresses to work with
264 * @return mapping - containing the different recipient types
267 private mapping resolve_recipients(array names)
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);
276 mapping result =([ "groups":({}), "remote":({}), "users":({}),
281 foreach(resolved;; string target)
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 });
296 unresolved += ({ target });
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);
311 * within an array of adresses, search for aliases and replace them with
314 * @param array names - the addresses to work with
315 * @return array the input-array with aliases replaced by targets
318 private array replace_aliases(array names)
321 foreach(names;; string name)
323 if(arrayp(mAliases[name])) //add code for checking aliases on aliases here...
324 result+=mAliases[name];
328 return Array.uniq(result);
335 * within an array of adresses, search for forwards and replace them with
338 * @param array names - the addresses to work with
339 * @return array the input-array with forwards replaced by targets
342 private array replace_forwards(array names, void|mapping fwd_done)
346 if ( !mappingp(fwd_done) )
349 for(int i=0;i<sizeof(names);i++)
351 if ( fwd_done[names[i]] )
353 if(arrayp(mForwards[names[i]]))
355 array tmp=mForwards[names[i]];
356 for(int j=0;j<sizeof(tmp);j++)
358 if ( !stringp(tmp[j]) )
361 fwd_done[tmp[j]] = 1;
362 if(search(tmp[j],"@")!=-1) //remote address
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
370 array tmp2=replace_aliases( ({tmp[j]}) );
371 result+=replace_forwards(tmp2, fwd_done);
376 else result+=({names[i]});
384 * send a message to multiple recipients
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
390 int send_message(array target, Messaging.Message msg)
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);
399 array asLocal=resolved-asRemote;
400 if(sizeof(asLocal)>0) hasLocal=1;
402 send_local(asLocal,msg);
406 LOG_FORWARD("Sending to " + sizeof(asRemote) + " Remote Recipients !");
408 asRemote = Array.uniq(asRemote);
409 send_remote(asRemote, rawText, sSender);
411 for(int i=0;i<sizeof(asRemote);i++)
412 send_remote(asRemote[i],rawText,sSender);
414 return 1; //success, add code for failures!
417 object lookup_sender(string sender)
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
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");
433 if (sizeof(users) > 0)
440 * send a message (rfc2822 raw text) to multiple recipients
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
447 int send_message_raw(array target,
450 void|object messageObj)
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);
462 err = catch(res=send_users(sendAs->users, messageObj));
465 FATAL("Error while sending message to users: %O, %O", err[0], err[1]);
467 LOG_FORWARD("Warning! send_message_raw failed on one ore more recipient users!");
470 if(sizeof(sendAs->objects)) {
471 if (!objectp(messageObj))
472 messageObj = Messaging.MIME2Message(rawText);
473 err = catch(res=send_objects(sendAs->objects, messageObj));
476 FATAL("Error while sending message to objects: %O,%O", err[0], err[1]);
479 LOG_FORWARD("Warning! send_message_raw failed on one ore more recipient objects!");
481 send_remote(sendAs->remote, rawText, envFrom);
482 foreach(sendAs->groups;; object target)
483 send_group(target,rawText,envFrom,messageObj);
485 MESSAGE("Message to %s send in %f seconds", (target*","), tt);
491 * send a message to objects
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
498 private int send_objects(array targets, Messaging.Message msg)
500 LOG_FORWARD("send_objects(%O, %O)\n", targets, msg->header()->subject);
501 if(sizeof(targets)==0 || !arrayp(targets)) return 0;
505 seteuid(USER("root"));
506 foreach(targets; int count; object target)
508 Messaging.Message copy;
509 if(count<sizeof(targets)-1)
510 // duplicate message if more than one recipient
511 copy=msg->duplicate();
513 // last recipient gets original
516 mixed err=catch{Messaging.add_message_to_object(copy,target);};
522 FATAL("unable to add message to: %s(%d):%O", target->get_identifier(), target->get_object_id(), err);
528 LOG_FORWARD("Warning!! send_objects encountered errors - some recipients failed!");
538 * send a message to users
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
545 private int send_users(array targets, Messaging.Message msg)
547 if(sizeof(targets)==0 || !arrayp(targets)) return 0;
549 foreach(targets; int count; object user)
551 if ( !objectp(user) )
553 Messaging.Message copy;
554 if(count<sizeof(targets)-1)
555 copy=msg->duplicate();
557 copy=msg; // last recipient gets original
559 //the recipient gets all rights on his/her copy
560 copy->grant_access(user);
562 Messaging.BaseMailBox box = Messaging.get_mailbox(user);
563 box->add_message(copy);
573 * create headers to be added to annotations sent as mails
575 * @param object group - the group to send the message to
576 * @return mapping of headers
578 mapping create_list_headers(object group)
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")));
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);
590 headers["X-Steam-Annotates"] = _Server->query_config("web_server")+":"
591 +(string)group_workroom->get_object_id();
596 * send a message to a group
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
603 private int send_group(object group,
606 void|object messageobj)
608 LOG_FORWARD("send_group(%O)\n", group);
609 mapping headers = create_list_headers(group);
614 rawmessage = (((array)headers)[*]*": ")*"\r\n" + "\r\n" + msg;
615 if (!objectp(messageobj))
616 messageobj = Messaging.MIME2Message(rawmessage);
618 //string messages come from outside, need to be added to group
619 send_objects( ({ group->query_attribute(GROUP_WORKROOM) }), messageobj);
621 else if(objectp(msg))
623 messageobj = Messaging.Message(msg);
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();
633 FATAL("Warning! unknown message format: %O", msg);
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);
643 if ( sizeof(fromUsers) > 0 )
644 fromUser = fromUsers[0];
645 if ( !objectp(fromUser) ) {
646 FATAL("Unknown Sending User %O - not relayed", sender);
649 if ( settings == "closed" && !group->is_member(fromUser) ) {
650 FATAL("User %O is not a member of group %O - not relayed",
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);
662 // store message on group
664 group->add_annotation(messageobj->get_msg_object()->duplicate());
667 FATAL("Failed to store annotation on group: %O\n%O", err[0], err[1]);
677 * send a message to a subgroup
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
685 private int send_subgroup(object group, object parent, string msg, string envFrom)
687 mapping headers = ([]);
688 headers["X-sTeam-Subgroup"] = group->get_identifier();
689 msg = (((array)headers)[*]*": ")*"\r\n" + "\r\n" + msg;
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);
695 array subgroups = group->get_sub_groups();
696 foreach(subgroups;; object subgroup)
697 send_subgroup(subgroup, parent, msg, envFrom);
704 * send a simple message (subject & text) to multiple recipients
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
711 int send_message_simple(array target, string subject, string message)
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;
723 send_local(asLocal,msg);
726 asRemote = Array.uniq(asRemote);
727 send_remote(asRemote, rawText, sSender);
729 for(int i=0;i<sizeof(asRemote);i++)
730 send_remote(asRemote[i],rawText,sSender);
732 return 1; //success, add code for failures!
736 * replace group-entries with members of group
737 * object ids included in input are not changed by this
739 * @param array target - the addresses to replace groups in
740 * @return array input-array with groups replaced by members of groups
743 private array expand_local_addresses(array target)
746 for(int i=0;i<sizeof(target);i++)
748 if(objectp(MODULE_USERS->lookup(target[i])))
750 result+=({target[i]});
753 // FIXME: this is dead code! all groups should have been removed by now.
754 object tmp=MODULE_GROUPS->lookup(target[i]);
757 result+=tmp->get_members();
760 if(sscanf(target[i],"%d",int oid))
762 result+=({target[i]});
765 LOG_FORWARD("expand_local_addresses: failed to find \""+target[i]+"\"");
767 return result; //now contains only user-names and object-ids
773 private int send_local_single(string recipient, Messaging.Message msg)
776 if(sscanf(recipient,"%d",oid)!=1)
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);
783 else //store message on object
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);
792 Messaging.add_message_to_object(msg,target);
799 * send a message to local recipients
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
806 private int send_local(array target, Messaging.Message msg)
808 if(sizeof(target)==0 || !arrayp(target)) return 0;
810 int result=1; //success
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);
816 for(int i=sizeof(asLocal)-1;i>0;i--) //duplicate message if more than one recipient
818 Messaging.Message copy=msg->duplicate();
819 if(send_local_single(asLocal[i],copy)==0)
821 LOG_FORWARD("failed to send message #"+copy->get_object_id()+" to: "+asLocal[i]);
827 if(send_local_single(asLocal[0],msg)==0) //last recipient gets "original" message
829 LOG_FORWARD("failed to send message #"+msg->get_object_id()+" to: "+asLocal[0]);
835 if(result==0) LOG_FORWARD("Warning!! send_local encountered errors - some recipients failed!");
843 * send a message to a remote address (-> send e-mail)
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
851 private int send_remote(string|array address, string rawText, string envFrom)
854 if( arrayp(address) && sizeof(address)==0 ) return 1;
855 if( stringp(address) && strlen(address) == 0 ) return 1;
857 if( sscanf(envFrom,"%*s<%s>",fixed) == 0 )
859 if ( search( envFrom, '@' ) >= 0 )
862 FATAL("send_remote: illegal envFrom! : "+envFrom);
867 if ( arrayp(address) && (l=sizeof(address)) > 10 ) {
868 for ( int i = 0; i < sizeof(address); i+=10 ) {
871 users = address[i..];
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));
879 get_module("smtp")->send_mail_raw(address, rawText, fixed);
880 LOG_FORWARD("Message delivered to " + sprintf("%O", address));
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.
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
901 int low_add_alias(string alias, string target)
903 if ( (arrayp(mAliases[alias]) && search(mAliases[alias], target)>=0) )
904 return 1; // target was already set for that alias
906 if ( !arrayp(mAliases[alias]) )
907 mAliases[alias] = ({ });
909 mAliases[alias]+=({target});
915 int add_alias(string alias, string target)
917 _SECURITY->access_write(0, this_object(), CALLER);
918 return low_add_alias(alias, target);
922 * remove an alias from the system aliases
924 * @param string alias - the alias to delete
925 * @return 1 if successful, 0 if alias does not exist
929 int low_delete_alias(string alias)
931 if(arrayp(mAliases[alias]))
933 m_delete(mAliases,alias);
941 int delete_alias(string alias)
943 _SECURITY->access_write(0, this_object(), CALLER);
944 return low_delete_alias(alias);
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)
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
959 int add_forward(object user, string forward, int|void no_more)
961 _SECURITY->access_write(0, user, CALLER);
963 LOG_FORWARD("adding forward for %s:%s", user->get_user_name(), forward);
964 if ( !stringp(forward) ) {
965 FATAL("invalid forward: %O", forward);
969 if(intp(no_more) && no_more==1) forward="/"+forward;
970 if(user->get_object_class() & CLASS_USER)
972 string name=user->get_identifier();
973 if ( forward == name )
974 steam_error("add_forward: Unable to resolve forward to itself !");
976 if ( mForwards[name] && search( mForwards[name], forward ) >= 0 ) {
977 LOG_FORWARD("add_forward(%s : %s) : forward already exists",
979 return 0; // forward already exists
981 mForwards[name]+=({forward});
986 LOG_FORWARD("ERROR, add_forward() called for non-user object #"
987 +user->get_object_id());
993 * remove a user-forward
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
1000 int delete_forward(object user, void|string forward)
1002 _SECURITY->access_write(0, user, CALLER);
1004 if ( ! objectp(user) || !(user->get_object_class() && CLASS_USER) ) {
1005 LOG_FORWARD("delete_forward: invalid user %O", user);
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
1015 if ( zero_type(forward) ) { // delete all forwards for user
1016 LOG_FORWARD("deleting forwards for %s", name);
1017 m_delete(mForwards,name);
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);
1025 mForwards[name] -= ({ forward });
1031 * get the current forward for a user
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
1036 array get_forward(object user)
1038 if(user->get_object_class() & CLASS_USER)
1039 return mForwards[user->get_identifier()];
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);