~ubuntu-branches/ubuntu/wily/steam/wily

« back to all changes in this revision

Viewing changes to server/modules/forward.pike

  • Committer: Package Import Robot
  • Author(s): Felix Geyer
  • Date: 2013-10-29 19:51:18 UTC
  • mfrom: (1.1.4) (0.1.4 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20131029195118-b9bxciz5hwx5z459
Tags: 1:1.0.0.39-2ubuntu1
Add an epoch to the version number as there was an unrelated steam package
in the archive with a higher version number.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
 
18
 
/**
19
 
 * This module is responsible for sending messages to users/groups/... and
20
 
 * e-mails to remote adresses. It also keeps track of system-wide
21
 
 * alias-adresses and of user-forwards.
22
 
 */
23
 
 
24
 
inherit "/kernel/module";
25
 
 
26
 
#include <database.h>
27
 
#include <macros.h>
28
 
#include <classes.h>
29
 
#include <config.h>
30
 
#include <attributes.h>
31
 
#include <events.h>
32
 
 
33
 
//#define DEBUG_FORWARD
34
 
 
35
 
#ifdef DEBUG_FORWARD
36
 
#define LOG_FORWARD(s, args...) werror("forward: "+s+"\n", args)
37
 
#else
38
 
#define LOG_FORWARD
39
 
#endif
40
 
 
41
 
#if constant(Protocols.SMTP.client) 
42
 
#define SMTPCLIENT Protocols.SMTP.client
43
 
#else
44
 
#define SMTPCLIENT Protocols.SMTP.Client
45
 
#endif
46
 
 
47
 
//stores aliases & forwards
48
 
static mapping(string:array) mAliases, mForwards;
49
 
static mapping(object:object) mListeners;
50
 
 
51
 
string _mailserver;
52
 
int _mailport;
53
 
 
54
 
string get_mask_char() { return "/";}
55
 
 
56
 
void init_module()
57
 
{
58
 
    mAliases=([]);
59
 
    mForwards=([]);
60
 
    mListeners=([]);
61
 
    add_data_storage(STORE_FORWARD,retrieve_aliases,restore_aliases);
62
 
}
63
 
 
64
 
static array|void get_parent_groups(object group)
65
 
{
66
 
  object parent = group->get_parent();
67
 
  array parents = ({});
68
 
  if(!objectp(parent))
69
 
    return;
70
 
  else
71
 
    parents += ({ parent });
72
 
  array grandparents=get_parent_groups(parent);
73
 
  if(arrayp(grandparents) )
74
 
    parents += grandparents;
75
 
  return parents;
76
 
}
77
 
 
78
 
static array get_user_groups(object user)
79
 
{
80
 
    array groups;
81
 
    array usergroups = groups = user->get_groups();
82
 
    foreach(usergroups;; object group)
83
 
    {
84
 
      array parents = get_parent_groups(group);
85
 
      if(arrayp(parents))
86
 
        groups += parents;
87
 
    }
88
 
    return(Array.uniq(groups));
89
 
}
90
 
 
91
 
void load_module()
92
 
93
 
  add_global_event(EVENT_ADD_MEMBER, user_join_group, PHASE_NOTIFY);
94
 
  array groups = ({});
95
 
  LOG_FORWARD("forwards are: %O\n", mForwards);
96
 
  foreach(mForwards; string user;)
97
 
  {
98
 
    groups += get_user_groups(MODULE_USERS->lookup(user));
99
 
  }
100
 
  groups = Array.uniq(groups);
101
 
  LOG_FORWARD("listen to groups: %O\n", groups->get_identifier());
102
 
  foreach(groups;; object group)
103
 
  {
104
 
    listen_annotations(group);
105
 
  }
106
 
  // always provide an abuse adress:
107
 
  add_alias( "abuse", "admin" );
108
 
}
109
 
 
110
 
static void user_join_group(int event, object group, object caller, object user)
111
 
{
112
 
  if(!objectp(mListeners[group]) && mForwards[user->get_user_name()])
113
 
    listen_annotations(group);
114
 
}
115
 
 
116
 
void listen_annotations(object group)
117
 
{
118
 
  if(!objectp(mListeners[group]))
119
 
  {
120
 
    mListeners[group] = 
121
 
      add_event(group->query_attribute(GROUP_WORKROOM), 
122
 
                EVENT_ANNOTATE|EVENTS_MONITORED, PHASE_NOTIFY, 
123
 
                lambda(int event, mixed ... args)
124
 
                { 
125
 
                    send_annotation_remote(event, group, @args); 
126
 
                });
127
 
  }
128
 
  else
129
 
  {
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());
133
 
  }
134
 
}
135
 
 
136
 
static void send_annotation_remote(int event, object group, object annotated, 
137
 
                                   object caller, object ... thread)
138
 
{
139
 
  // find root of thread...
140
 
  object parent = thread[-1];
141
 
  while ( objectp(parent->get_annotating()) ) 
142
 
  {
143
 
    parent = parent->get_annotating();
144
 
  }
145
 
 
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());
150
 
 
151
 
  if(parent==annotated)
152
 
    send_group(group, thread[-1]); 
153
 
}
154
 
 
155
 
void install_module()
156
 
{
157
 
    _mailserver = _Server->query_config(CFG_MAILSERVER);
158
 
    _mailport = (int)_Server->query_config(CFG_MAILPORT);
159
 
    LOG_FORWARD("mailserver is: "+_mailserver+":"+_mailport);
160
 
}    
161
 
 
162
 
string get_identifier() { return "forward"; }
163
 
 
164
 
mapping get_aliases()
165
 
{
166
 
    return mAliases;
167
 
}
168
 
 
169
 
array get_alias ( string key ) {
170
 
  if ( arrayp(mAliases[key]) )
171
 
    return copy_value( mAliases[key] );
172
 
  else
173
 
    return UNDEFINED;
174
 
}
175
 
 
176
 
mapping retrieve_aliases()
177
 
{
178
 
    if ( CALLER != _Database )
179
 
            THROW("Caller is not database !", E_ACCESS);
180
 
 
181
 
    return (["aliases" : mAliases, "forwards" : mForwards]);
182
 
}
183
 
 
184
 
void restore_aliases(mapping data)
185
 
{
186
 
    if ( CALLER != _Database )
187
 
            THROW("Caller is not database !", E_ACCESS);
188
 
 
189
 
    mAliases=data["aliases"];
190
 
    mForwards=data["forwards"];
191
 
    
192
 
    LOG_FORWARD("loaded "+sizeof(mAliases)+" aliases and "
193
 
                +sizeof(mForwards)+" forwards");
194
 
}
195
 
 
196
 
/**
197
 
 * check if an address is valid
198
 
 *
199
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
200
 
 * @param string address - the address to check
201
 
 * @return int 1 if valid, 0 if invalid, -1 if access denied
202
 
 */
203
 
int is_valid(string address)
204
 
{
205
 
    LOG_FORWARD("checking adress \"%O\"",address);
206
 
    LOG_FORWARD("alias...");
207
 
    if(arrayp(mAliases[address]))
208
 
        return 1; //adress is alias
209
 
    LOG_FORWARD("no - trying user...");
210
 
    if(objectp(MODULE_USERS->lookup(address)))
211
 
        return 1; //address is single user
212
 
    LOG_FORWARD("no - trying group...");
213
 
    if ( address == "steam" )
214
 
      return 0; // no mailing to steam-group allowed
215
 
    if(objectp(MODULE_GROUPS->lookup(address)))
216
 
        return 1; //adress is a group
217
 
    LOG_FORWARD("no - trying to replace - with space...");
218
 
    if(objectp(MODULE_GROUPS->lookup(replace(address, "-", " "))))
219
 
        return 1; //adress is a group
220
 
    LOG_FORWARD("no - trying object-id...");
221
 
    if(sscanf(address,"%d",int oid))
222
 
    {
223
 
        LOG_FORWARD("looking for object #%O",oid);
224
 
        object tmp=_Database->find_object(oid);
225
 
        if(objectp(tmp))
226
 
        {
227
 
            LOG_FORWARD("checking access on object #%O",oid);
228
 
            mixed err = catch { _SECURITY->access_annotate(0, tmp, CALLER, 0); };
229
 
            if(err!=0) return -1; //access denied -> invalid target-address
230
 
            else return 1; //target is existing object & annotatable
231
 
        }
232
 
        else LOG_FORWARD("not found");
233
 
    }
234
 
    LOG_FORWARD("sorry - no valid target found!");
235
 
    
236
 
    return 0; //no checks succeeded, target address is invalid
237
 
}
238
 
 
239
 
/**
240
 
 * return the remote addresses within an array of mixed addresses
241
 
 *
242
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
243
 
 * @param array(string) names - the addresses to get remotes from
244
 
 * @return array(string) the remote adresses
245
 
 */
246
 
private array(string) get_remote_addresses(array(string) names)
247
 
{
248
 
    array(string) result=({});
249
 
    for(int i=0;i<sizeof(names);i++)
250
 
        if(search(names[i],"@")!=-1)
251
 
            result+=({names[i]});
252
 
    return result;
253
 
}
254
 
 
255
 
string resolve_name(object grp)
256
 
{
257
 
  if ( objectp(grp) ) {
258
 
    if ( grp->get_object_class() & CLASS_USER )
259
 
      return grp->get_user_name();
260
 
    return grp->get_identifier();
261
 
  }
262
 
  return "";
263
 
}
264
 
 
265
 
/**
266
 
 * split targets into remote, groups, users, and objects
267
 
 *
268
 
 * @author Martin B�hr
269
 
 * @param array(string) names - the addresses to work with
270
 
 * @return mapping - containing the different recipient types
271
 
 */
272
 
private mapping resolve_recipients(array(string) names)
273
 
{
274
 
    array unresolved=({});
275
 
    array(string) resolved=replace_aliases(names);
276
 
    resolved=replace_forwards(resolved);
277
 
   
278
 
    mapping result =([ "groups":({}), "remote":({}), "users":({}), 
279
 
                       "objects":({}) ]);
280
 
    object target_obj;
281
 
    int oid;
282
 
   
283
 
    foreach(resolved;; string target)
284
 
    {
285
 
        if ( !stringp(target) )
286
 
            continue;
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 });
299
 
        else
300
 
            unresolved += ({ target });
301
 
    }
302
 
   
303
 
    if(sizeof(unresolved))
304
 
        FATAL("Warning! unresolved addresses: %O", unresolved);
305
 
    return result;
306
 
}
307
 
 
308
 
/**
309
 
 * within an array of adresses, search for aliases and replace them with 
310
 
 * their targets
311
 
 *
312
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
313
 
 * @param array(string) names - the addresses to work with
314
 
 * @return array(string) the input-array with aliases replaced by targets
315
 
 */
316
 
private array(string) replace_aliases(array(string) names)
317
 
{
318
 
    array(string) result=({});
319
 
    foreach(names;; string name)
320
 
    {
321
 
        if(arrayp(mAliases[name])) //add code for checking aliases on aliases here...
322
 
            result+=mAliases[name];
323
 
        else 
324
 
           result+=({name});
325
 
    }
326
 
    return Array.uniq(result);
327
 
}
328
 
 
329
 
 
330
 
/**
331
 
 * within an array of adresses, search for forwards and replace them with 
332
 
 * their targets
333
 
 *
334
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
335
 
 * @param array(string) names - the addresses to work with
336
 
 * @return array(string) the input-array with forwards replaced by targets
337
 
 */
338
 
private array(string) replace_forwards(array(string) names, void|mapping fwd_done)
339
 
{
340
 
    array(string) result=({});
341
 
 
342
 
    if ( !mappingp(fwd_done) )
343
 
      fwd_done = ([ ]);
344
 
 
345
 
    for(int i=0;i<sizeof(names);i++)
346
 
    {
347
 
        if ( fwd_done[names[i]] )
348
 
          continue;
349
 
        if(arrayp(mForwards[names[i]]))
350
 
        {
351
 
            array(string) tmp=mForwards[names[i]];
352
 
            for(int j=0;j<sizeof(tmp);j++)
353
 
            {
354
 
                if ( !stringp(tmp[j]) )
355
 
                    continue;
356
 
                
357
 
                fwd_done[tmp[j]] = 1;
358
 
                if(search(tmp[j],"@")!=-1) //remote address
359
 
                    result+=({tmp[j]});
360
 
                else
361
 
                {
362
 
                    if(search(tmp[j],"/")!=-1) //local forward-target starts with "/" -> don't forward further
363
 
                        result+=({tmp[j]-"/"});
364
 
                    else //lookup forward of this forward-target
365
 
                    {
366
 
                        array(string) tmp2=replace_aliases( ({tmp[j]}) );
367
 
                        result+=replace_forwards(tmp2, fwd_done);
368
 
                    }
369
 
                }
370
 
            }
371
 
        }
372
 
        else result+=({names[i]});
373
 
    }
374
 
    return result;
375
 
}
376
 
 
377
 
/**
378
 
 * send a message to multiple recipients
379
 
 *
380
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
381
 
 * @param array(string) target - the addresses to send the message to
382
 
 * @param Message msg - the message to send (WARNING msg is destructed by sending!)
383
 
 * @return int 1 if successful
384
 
 */
385
 
int send_message(array(string) target, Messaging.Message msg)
386
 
{
387
 
    int hasLocal=0;
388
 
    string rawText=msg->complete_text();
389
 
    string sSender=msg->sender();
390
 
    array(string) resolved=replace_aliases(target);
391
 
    resolved=replace_forwards(resolved);
392
 
    array(string) asRemote=get_remote_addresses(resolved);
393
 
 
394
 
    array(string) asLocal=resolved-asRemote;
395
 
    if(sizeof(asLocal)>0) hasLocal=1;
396
 
    if(hasLocal)
397
 
        send_local(asLocal,msg);
398
 
    else 
399
 
        destruct(msg);
400
 
    
401
 
    LOG_FORWARD("Sending to " + sizeof(asRemote) + " Remote Recipients !");
402
 
    
403
 
    asRemote = Array.uniq(asRemote);
404
 
    send_remote(asRemote, rawText, sSender);
405
 
    return 1;
406
 
    for(int i=0;i<sizeof(asRemote);i++)
407
 
        send_remote(asRemote[i],rawText,sSender);
408
 
        
409
 
    return 1; //success, add code for failures!
410
 
}
411
 
 
412
 
/**
413
 
 * send a message (rfc2822 raw text) to multiple recipients
414
 
 *
415
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
416
 
 * @param array(string) target - the addresses to send the message to
417
 
 * @param string rawText - the text of the message (rfc2822-format!)
418
 
 * @param string|void envFrom - the sender-value of the SMTP-envelope (only needed for forwarding, may be left empty)
419
 
 * @return int 1 if successful
420
 
 */
421
 
int send_message_raw(array(string) target, string rawText, string envFrom)
422
 
{
423
 
    mixed err;
424
 
    LOG_FORWARD("Forward: send_message_raw(%O)", target);
425
 
    mapping sendAs = resolve_recipients(target);
426
 
    int res=1; //success
427
 
    if(sizeof(sendAs->users))
428
 
        err = catch(res=send_users(sendAs->users,Messaging.MIME2Message(rawText)));   
429
 
    if ( err ) 
430
 
      FATAL("Error while sending message to users: %O, %O", err[0], err[1]);
431
 
    if (res==0) 
432
 
      LOG_FORWARD("Warning! send_message_raw failed on one ore more recipient users!");
433
 
    res=1;
434
 
 
435
 
    if(sizeof(sendAs->objects))
436
 
        err = catch(res=send_objects(sendAs->objects,Messaging.MIME2Message(rawText)));   
437
 
    if ( err ) 
438
 
      FATAL("Error while sending message to objects: %O,%O", err[0], err[1]);
439
 
 
440
 
    if (res==0) 
441
 
      LOG_FORWARD("Warning! send_message_raw failed on one ore more recipient objects!");
442
 
 
443
 
    send_remote(sendAs->remote, rawText, envFrom);
444
 
    foreach(sendAs->groups;; object target)
445
 
        send_group(target,rawText,envFrom);
446
 
 
447
 
    return res;
448
 
}
449
 
 
450
 
/**
451
 
 * send a message to objects
452
 
 *
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
457
 
 */
458
 
private int send_objects(array(object) targets, Messaging.Message msg)
459
 
{
460
 
    LOG_FORWARD("send_objects(%O, %O)\n", targets, msg->header()->subject);
461
 
    if(sizeof(targets)==0 || !arrayp(targets)) return 0;
462
 
 
463
 
    int errors;
464
 
 
465
 
    seteuid(USER("root"));
466
 
    foreach(targets; int count; object target)
467
 
    {
468
 
        Messaging.Message copy;
469
 
        if(count<sizeof(targets)-1)
470
 
            // duplicate message if more than one recipient
471
 
            copy=msg->duplicate();
472
 
        else
473
 
            // last recipient gets original
474
 
            copy=msg;
475
 
 
476
 
        mixed err=catch{Messaging.add_message_to_object(copy,target);};
477
 
        if(err)
478
 
        {
479
 
            copy->delete();
480
 
            destruct(copy);
481
 
            errors++;
482
 
            FATAL("unable to add message to: %s(%d):%O", target->get_identifier(), target->get_object_id(), err);
483
 
        }
484
 
    }
485
 
 
486
 
    if(errors) 
487
 
    {
488
 
      LOG_FORWARD("Warning!! send_objects encountered errors - some recipients failed!");
489
 
      return 0;
490
 
    }
491
 
 
492
 
    return 1;
493
 
}
494
 
 
495
 
/**
496
 
 * send a message to users
497
 
 *
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
502
 
 */
503
 
private int send_users(array(object) targets, Messaging.Message msg)
504
 
{
505
 
    if(sizeof(targets)==0 || !arrayp(targets)) return 0;
506
 
 
507
 
    foreach(targets; int count; object user)
508
 
    {
509
 
        if ( !objectp(user) )
510
 
            continue;
511
 
        Messaging.Message copy=msg->duplicate();
512
 
        if(count<sizeof(targets)-1)
513
 
            // duplicate message if more than one recipient
514
 
            copy=msg->duplicate();
515
 
        else
516
 
            // last recipient gets original
517
 
            copy=msg;
518
 
 
519
 
        //the recipient gets all rights on his/her copy
520
 
        copy->grant_access(user); 
521
 
 
522
 
        Messaging.BaseMailBox box = Messaging.get_mailbox(user);
523
 
        copy->this()->set_acquire(box->this());
524
 
        box->add_message(copy);
525
 
    }
526
 
 
527
 
    seteuid(0);
528
 
    return 1;
529
 
}
530
 
 
531
 
/**
532
 
 * create headers to be added to annotations sent as mails
533
 
 *
534
 
 * @author Martin B�hr
535
 
 * @param object group - the group to send the message to
536
 
 * @return mapping of headers
537
 
 */
538
 
mapping create_list_headers(object group)
539
 
{
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")));
544
 
  
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);
549
 
  
550
 
    headers["X-Steam-Annotates"] = _Server->query_config("web_server")+":"
551
 
                                   +(string)group_workroom->get_object_id();
552
 
    return headers;
553
 
}
554
 
 
555
 
/**
556
 
 * send a message to a group
557
 
 *
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
562
 
 */
563
 
private int send_group(object group, string|object msg, string|void envFrom)
564
 
{
565
 
    LOG_FORWARD("send_group(%O)\n", group);
566
 
    mapping headers = create_list_headers(group);
567
 
    string rawmessage;
568
 
    object messageobj;
569
 
    if(stringp(msg))
570
 
    {
571
 
        rawmessage = (((array)headers)[*]*": ")*"\r\n" + "\r\n" + msg;
572
 
        messageobj = Messaging.MIME2Message(rawmessage);
573
 
 
574
 
        //string messages come from outside, need to be added to group
575
 
        send_objects( ({ group->query_attribute(GROUP_WORKROOM) }), messageobj);
576
 
    }
577
 
    else if(objectp(msg))
578
 
    {
579
 
        messageobj = Messaging.Message(msg);
580
 
        if(!envFrom)
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();
586
 
    }
587
 
    else
588
 
    {
589
 
        FATAL("Warning! unknown message format: %O", msg);
590
 
        return 0;
591
 
    }
592
 
    
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);  
596
 
    
597
 
    return 1;
598
 
}
599
 
 
600
 
/**
601
 
 * send a message to a subgroup
602
 
 *
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
608
 
 */
609
 
private int send_subgroup(object group, object parent, string msg, string envFrom)
610
 
{
611
 
  mapping headers = ([]);
612
 
  headers["X-sTeam-Subgroup"] = group->get_identifier();
613
 
  msg = (((array)headers)[*]*": ")*"\r\n" + "\r\n" + msg;
614
 
 
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);  
618
 
  
619
 
  array subgroups = group->get_sub_groups();
620
 
  foreach(subgroups;; object subgroup)
621
 
    send_subgroup(subgroup, parent, msg, envFrom);
622
 
}
623
 
 
624
 
 
625
 
/**
626
 
 * send a simple message (subject & text) to multiple recipients
627
 
 *
628
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
629
 
 * @param array(string) target - the addresses to send the message to
630
 
 * @param string subject - the subject of the message to send
631
 
 * @param string message - the text of the message to send
632
 
 * @return int 1 if successful
633
 
 */
634
 
int send_message_simple(array(string) target, string subject, string message)
635
 
{
636
 
    int hasLocal=0;
637
 
    Messaging.Message msg = Messaging.SimpleMessage(target, subject, message);
638
 
    string rawText=msg->complete_text();
639
 
    string sSender=msg->sender();
640
 
    array(string) resolved=replace_aliases(target);
641
 
    resolved=replace_forwards(resolved);
642
 
    array(string) asRemote=get_remote_addresses(resolved);
643
 
    array(string) asLocal=resolved-asRemote;
644
 
    if(sizeof(asLocal)>0) hasLocal=1;
645
 
    if(hasLocal)
646
 
        send_local(asLocal,msg);
647
 
    else destruct(msg);
648
 
 
649
 
    asRemote = Array.uniq(asRemote);
650
 
    send_remote(asRemote, rawText, sSender);
651
 
    return 1;
652
 
    for(int i=0;i<sizeof(asRemote);i++)
653
 
        send_remote(asRemote[i],rawText,sSender);
654
 
        
655
 
    return 1; //success, add code for failures!
656
 
}
657
 
 
658
 
/**
659
 
 * replace group-entries with members of group
660
 
 * object ids included in input are not changed by this
661
 
 *
662
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
663
 
 * @param array(string) target - the addresses to replace groups in
664
 
 * @return array(string) input-array with groups replaced by members of groups
665
 
 */
666
 
private array(string) expand_local_addresses(array(string) target)
667
 
{
668
 
    array(string) result=({});
669
 
    for(int i=0;i<sizeof(target);i++)
670
 
    {
671
 
        if(objectp(MODULE_USERS->lookup(target[i])))
672
 
        {
673
 
            result+=({target[i]});
674
 
            continue;
675
 
        }
676
 
        // FIXME: this is dead code! all groups should have been removed by now.
677
 
        object tmp=MODULE_GROUPS->lookup(target[i]);
678
 
        if(objectp(tmp))
679
 
        {
680
 
            result+=tmp->get_members();
681
 
            continue;
682
 
        }
683
 
        if(sscanf(target[i],"%d",int oid))
684
 
        {
685
 
            result+=({target[i]});
686
 
            continue;
687
 
        }
688
 
        LOG_FORWARD("expand_local_addresses: failed to find \""+target[i]+"\"");
689
 
    }
690
 
    return result; //now contains only user-names and object-ids
691
 
}
692
 
 
693
 
private int send_local_single(string recipient, Messaging.Message msg)
694
 
{
695
 
        int oid;
696
 
        if(!sscanf(recipient,"%d",oid)==1)
697
 
        {
698
 
            object user=MODULE_USERS->lookup(recipient);
699
 
            Messaging.BaseMailBox box = Messaging.get_mailbox(user);
700
 
            msg->grant_access(user); //the recipient gets all rights on his/her copy
701
 
            msg->this()->set_acquire(box->this());
702
 
            box->add_message(msg);
703
 
        }
704
 
        else //store message on object
705
 
        {
706
 
            object target=_Database->find_object(oid);
707
 
            if(!objectp(target)) return 0; //invalid object-id, do nothing
708
 
//            msg->grant_access(target->query_attribute(OBJ_OWNER));
709
 
            msg->this()->set_acquire(target); 
710
 
            Messaging.BaseMailBox box = Messaging.get_mailbox(target);
711
 
            if(objectp(box)) //target can be accessed as mailbox
712
 
                box->add_message(msg);
713
 
            else
714
 
                Messaging.add_message_to_object(msg,target);
715
 
        }
716
 
        return 1;
717
 
}
718
 
/**
719
 
 * send a message to local recipients
720
 
 *
721
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
722
 
 * @param array(string) target - the local addresses to send the message to
723
 
 * @param Message msg - the message to send (WARNING msg is destructed by sending!)
724
 
 * @return int 1 if successful
725
 
 */
726
 
private int send_local(array(string) target, Messaging.Message msg)
727
 
{
728
 
    if(sizeof(target)==0 || !arrayp(target)) return 0;
729
 
    
730
 
    int result=1; //success
731
 
 
732
 
    // FIXME: expand_local_addresses should not be needed anymore:
733
 
    array(string) asLocal=expand_local_addresses(target); //resolve aliases & forwards
734
 
    LOG_FORWARD("expanded local adresses are:%O",asLocal);
735
 
 
736
 
    for(int i=sizeof(asLocal)-1;i>0;i--) //duplicate message if more than one recipient
737
 
    {
738
 
        Messaging.Message copy=msg->duplicate();
739
 
        if(send_local_single(asLocal[i],copy)==0)
740
 
        {
741
 
            LOG_FORWARD("failed to send message #"+copy->get_object_id()+" to: "+asLocal[i]);
742
 
            copy->delete();
743
 
            destruct(copy);
744
 
            result=0;
745
 
        }
746
 
    }
747
 
    if(send_local_single(asLocal[0],msg)==0) //last recipient gets "original" message
748
 
    {
749
 
        LOG_FORWARD("failed to send message #"+msg->get_object_id()+" to: "+asLocal[0]);
750
 
        msg->delete();
751
 
        destruct(msg);
752
 
        result=0;
753
 
    }
754
 
 
755
 
    if(result==0) LOG_FORWARD("Warning!! send_local encountered errors - some recipients failed!");
756
 
 
757
 
    return result;
758
 
}
759
 
 
760
 
/**
761
 
 * send a message to a remote address (-> send e-mail)
762
 
 *
763
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
764
 
 * @param string address - the address to send the mail to
765
 
 * @param string rawText - the text of the message (rfc2822-format!)
766
 
 * @param string|void envFrom - the sender-value of the SMTP-envelope
767
 
 * @return int 1 if successful, 0 if not
768
 
 */
769
 
private int send_remote(string|array address, string rawText, string envFrom)
770
 
{
771
 
    string fixed;
772
 
    if( arrayp(address) && sizeof(address)==0 ) return 1;
773
 
    if( stringp(address) && strlen(address) == 0 ) return 1;
774
 
 
775
 
    if( sscanf(envFrom,"%*s<%s>",fixed) == 0 )
776
 
    {
777
 
        LOG_FORWARD("send_remote: illegal envFrom! : "+envFrom);
778
 
        return 0;
779
 
    }
780
 
    int l;
781
 
    if ( arrayp(address) && (l=sizeof(address)) > 10 ) {
782
 
        for ( int i = 0; i < sizeof(address); i+=10 ) {
783
 
            array users;
784
 
            if ( i + 10 >= l )
785
 
                users = address[i..];
786
 
            else
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));
790
 
        }
791
 
    }
792
 
    else {
793
 
        get_module("smtp")->send_mail_raw(address, rawText, fixed);
794
 
        LOG_FORWARD("Message delivered to " + sprintf("%O", address));
795
 
    }
796
 
    
797
 
    return 1;
798
 
}
799
 
 
800
 
/**
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.
806
 
 *
807
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
808
 
 * @param string alias - the name of the alias to add
809
 
 * @param string taget - the target the alias should point to
810
 
 * @return 1 if successful
811
 
 * @see delete_alias
812
 
 */
813
 
int add_alias(string alias, string target)
814
 
{
815
 
  if ( (arrayp(mAliases[alias]) && search(mAliases[alias], target)>=0) )
816
 
    return 1;  // target was already set for that alias
817
 
 
818
 
    if ( !arrayp(mAliases[alias]) )
819
 
        mAliases[alias] = ({ });
820
 
 
821
 
    mAliases[alias]+=({target});
822
 
    require_save();
823
 
    return 1;
824
 
}
825
 
 
826
 
/**
827
 
 * remove an alias from the system aliases
828
 
 *
829
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
830
 
 * @param string alias - the alias to delete
831
 
 * @return 1 if successful, 0 if alias does not exist
832
 
 * @see add_alias
833
 
 */
834
 
int delete_alias(string alias)
835
 
{
836
 
    if(arrayp(mAliases[alias]))
837
 
    {
838
 
        m_delete(mAliases,alias);
839
 
        require_save();
840
 
        return 1;
841
 
    }
842
 
    else return 0;
843
 
}
844
 
 
845
 
/**
846
 
 * add a forward for a specific user
847
 
 * if user already has a forward, it is extended by the given target
848
 
 * target may be a remote e-mail address or a local system address
849
 
 * (user, group, object-id, alias)
850
 
 *
851
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
852
 
 * @param object user - the user to add a forward for
853
 
 * @param string forward - the target address to add (e-mail or other valid system-address)
854
 
 * @param int|void no_more - set to 1, if this forward is "final", so mails get stored at this adress,
855
 
 *                           no matter if a forward exists for this target, too
856
 
 * @return int 1 if successful, 0 if not
857
 
 */
858
 
int add_forward(object user, string forward, int|void no_more)
859
 
{
860
 
    LOG_FORWARD("adding forward for %s:%s", user->get_user_name(), forward);
861
 
    if ( ! stringp(forward) ) {
862
 
      LOG_FORWARD("invalid forward: %O", forward);
863
 
      return 0;
864
 
    }
865
 
 
866
 
    if(intp(no_more) && no_more==1) forward="/"+forward;
867
 
    if(user->get_object_class() && CLASS_USER)
868
 
    {
869
 
        string name=user->get_identifier();
870
 
        if ( forward == name )
871
 
          steam_error("add_forward: Unable to resolve forward to itself !");
872
 
#if 0
873
 
        if(!mForwards[name]) //new user may be in groups we are not listening to
874
 
        {
875
 
          foreach(get_user_groups(user);; object group)
876
 
          {
877
 
            if(!mListeners[group])
878
 
              listen_annotations(group);
879
 
          }
880
 
        }
881
 
#endif 
882
 
        if ( mForwards[name] && search( mForwards[name], forward ) >= 0 ) {
883
 
          LOG_FORWARD("add_forward(%s : %s) : forward already exists",
884
 
                      name, forward);
885
 
          return 0;  // forward already exists
886
 
        }
887
 
        mForwards[name]+=({forward});
888
 
        require_save();
889
 
        return 1;
890
 
    }
891
 
    else
892
 
    {
893
 
        LOG_FORWARD("ERROR, add_forward() called for non-user object #"
894
 
                     +user->get_object_id());
895
 
        return 0;
896
 
    }
897
 
}
898
 
 
899
 
/**
900
 
 * remove a user-forward
901
 
 *
902
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
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)
906
 
 * @return int 1 if successful, 0 otherwise
907
 
 */
908
 
int delete_forward(object user, void|string forward)
909
 
{
910
 
  if ( ! objectp(user) || !(user->get_object_class() && CLASS_USER) ) {
911
 
    LOG_FORWARD("delete_forward: invalid user %O", user);
912
 
    return 0;
913
 
  }
914
 
 
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
919
 
  }
920
 
 
921
 
  if ( zero_type(forward) ) {  // delete all forwards for user
922
 
    LOG_FORWARD("deleting forwards for %s", name);
923
 
    m_delete(mForwards,name);
924
 
  }
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);
929
 
      return 0;
930
 
    }
931
 
    mForwards[name] -= ({ forward });
932
 
  }
933
 
  require_save();
934
 
  return 1;
935
 
}
936
 
 
937
 
/**
938
 
 * get the current forward for a user
939
 
 *
940
 
 * @author <a href="mailto:sepp@upb.de">Christian Schmidt</a>
941
 
 * @param object user - the user to get forward for
942
 
 * @return array(string) of forwards or 0 if user is not a sTeam-user-object
943
 
 */
944
 
array(string) get_forward(object user)
945
 
{
946
 
    if(user->get_object_class() & CLASS_USER)
947
 
        return mForwards[user->get_identifier()];
948
 
    else return 0;
949
 
}
950
 
 
951
 
/*
952
 
string dump_data()
953
 
{
954
 
    string res;
955
 
    res=sprintf("forwards:%O aliases:%O",mForwards,mAliases);
956
 
    LOG_FORWARD("current data of forward-module:\n"+res);
957
 
    LOG_FORWARD("mailserver is: "+_mailserver+":"+_mailport);
958
 
    return res;
959
 
}
960
 
*/