2004-07-23 20:40:34 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* $Id$
|
|
|
|
* Purpose ...............: Read *.msg messages
|
|
|
|
*
|
|
|
|
*****************************************************************************
|
2007-08-25 18:32:07 +00:00
|
|
|
* Copyright (C) 1997-2007
|
2004-07-23 20:40:34 +00:00
|
|
|
*
|
|
|
|
* Michiel Broek FIDO: 2:280/2802
|
|
|
|
* Beekmansbos 10
|
|
|
|
* 1971 BV IJmuiden
|
|
|
|
* the Netherlands
|
|
|
|
*
|
|
|
|
* This file is part of MBSE BBS.
|
|
|
|
*
|
|
|
|
* This BBS is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2, or (at your option) any
|
|
|
|
* later version.
|
|
|
|
*
|
|
|
|
* MBSE BBS is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with MBSE BBS; see the file COPYING. If not, write to the Free
|
|
|
|
* Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "../config.h"
|
|
|
|
#include "../lib/mbselib.h"
|
2004-07-24 15:32:30 +00:00
|
|
|
#include "../lib/users.h"
|
|
|
|
#include "../lib/mbsedb.h"
|
|
|
|
#include "../lib/msgtext.h"
|
|
|
|
#include "../lib/msg.h"
|
|
|
|
#include "msgflags.h"
|
2004-07-23 20:40:34 +00:00
|
|
|
#include "msg.h"
|
|
|
|
|
|
|
|
|
2004-07-23 21:24:09 +00:00
|
|
|
extern int net_msgs;
|
2004-07-24 19:18:11 +00:00
|
|
|
extern int net_in;
|
2004-07-24 19:30:26 +00:00
|
|
|
extern int net_imp;
|
2004-07-24 15:32:30 +00:00
|
|
|
extern int do_scan;
|
|
|
|
|
|
|
|
|
|
|
|
int toss_onemsg(char *);
|
2004-07-23 21:24:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
int toss_msgs(void)
|
|
|
|
{
|
|
|
|
DIR *dp;
|
|
|
|
struct dirent *de;
|
|
|
|
int files = 0;
|
|
|
|
|
2005-08-12 14:21:18 +00:00
|
|
|
if (strlen(CFG.msgs_path) == 0) {
|
|
|
|
WriteError("No path defined for *.msg mail, check setup 1.4 screen 2, item 10");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-07-23 21:24:09 +00:00
|
|
|
if ((dp = opendir(CFG.msgs_path)) == NULL) {
|
|
|
|
WriteError("$Can't opendir %s", CFG.msgs_path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
Syslog('m', "Process *.msg in %s", CFG.msgs_path);
|
|
|
|
IsDoing("Get *.msgs");
|
|
|
|
|
|
|
|
while ((de = readdir(dp))) {
|
|
|
|
if ((de->d_name[0] != '.') && (strstr(de->d_name, ".msg"))) {
|
|
|
|
if (IsSema((char *)"upsalarm")) {
|
|
|
|
Syslog('+', "Detected upsalarm semafore, aborting toss");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Syslog('m', "Process %s", de->d_name);
|
2004-07-24 15:32:30 +00:00
|
|
|
toss_onemsg(de->d_name);
|
2004-07-23 21:24:09 +00:00
|
|
|
files++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(dp);
|
|
|
|
|
|
|
|
if (files) {
|
|
|
|
Syslog('+',"Processed %d msg messages", files);
|
|
|
|
}
|
|
|
|
|
|
|
|
return files;
|
|
|
|
}
|
|
|
|
|
2004-07-23 20:40:34 +00:00
|
|
|
|
2004-07-24 15:32:30 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Toss one message and post into a JAM messagebase. Returns:
|
|
|
|
* 0 = Ok
|
|
|
|
* 1 = Can't open *.msg
|
|
|
|
* 2 = Can't read message
|
|
|
|
* 3 = Missing zone info
|
|
|
|
* 4 = No ftn network or netmailboard in setup
|
|
|
|
* 5 = Can't open JAM area
|
2005-02-03 20:23:29 +00:00
|
|
|
* 6 = Empty local netmail, dropped.
|
2004-07-24 15:32:30 +00:00
|
|
|
*/
|
|
|
|
int toss_onemsg(char *msgname)
|
|
|
|
{
|
2005-02-03 20:23:29 +00:00
|
|
|
int rc = 0, islocal, empty = TRUE;
|
2004-07-24 15:32:30 +00:00
|
|
|
char *temp, *dospath, *flagstr = NULL, *l, *r, *msgid = NULL;
|
2005-08-28 12:41:57 +00:00
|
|
|
char fromUserName[37], toUserName[37], subject[73], DateTime[21];
|
2004-07-24 15:32:30 +00:00
|
|
|
FILE *fp, *np;
|
|
|
|
faddr *ta;
|
2004-12-29 15:09:38 +00:00
|
|
|
unsigned char buf[0xbe];
|
2004-07-24 15:32:30 +00:00
|
|
|
unsigned short destNode = 0, destNet = 0, destZone = 0, destPoint = 0;
|
|
|
|
unsigned short origNode = 0, origNet = 0, origZone = 0, origPoint = 0;
|
|
|
|
unsigned short Attribute = 0;
|
|
|
|
struct stat sb;
|
|
|
|
|
2004-07-30 18:49:25 +00:00
|
|
|
net_msgs++;
|
2004-07-24 15:32:30 +00:00
|
|
|
temp = calloc(PATH_MAX, sizeof(char));
|
2005-08-28 14:10:06 +00:00
|
|
|
snprintf(temp, PATH_MAX, "%s/%s", CFG.msgs_path, msgname);
|
2004-07-24 15:32:30 +00:00
|
|
|
|
|
|
|
if ((fp = fopen(temp, "r")) == NULL) {
|
|
|
|
WriteError("$Can't open %s", temp);
|
|
|
|
free(temp);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First read message header information we need.
|
|
|
|
*/
|
|
|
|
memset(&fromUserName, 0, sizeof(fromUserName));
|
|
|
|
memset(&toUserName, 0, sizeof(toUserName));
|
|
|
|
memset(&subject, 0, sizeof(subject));
|
|
|
|
memset(&DateTime, 0, sizeof(DateTime));
|
|
|
|
|
|
|
|
if (fread(&buf, 1, 0xbe, fp) != 0xbe) {
|
|
|
|
WriteError("$Could not read header (%s)", temp);
|
|
|
|
fclose(fp);
|
|
|
|
free(temp);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2007-08-25 18:32:07 +00:00
|
|
|
strncpy(fromUserName, (char *)buf, 36);
|
|
|
|
strncpy(toUserName, (char *)buf+0x24, 36);
|
|
|
|
strncpy(subject, (char *)buf+0x48, 72);
|
|
|
|
strncpy(DateTime, (char *)buf+0x90, 20);
|
2004-07-24 15:32:30 +00:00
|
|
|
|
2004-09-25 11:19:22 +00:00
|
|
|
Syslog('m', "From \"%s\"", printable(fromUserName, 0));
|
|
|
|
Syslog('m', "To \"%s\"", printable(toUserName, 0));
|
|
|
|
Syslog('m', "Subj \"%s\"", printable(subject, 0));
|
|
|
|
Syslog('m', "Date \"%s\"", printable(DateTime, 0));
|
2004-07-24 15:32:30 +00:00
|
|
|
|
|
|
|
destNode = (buf[0xa7] << 8) + buf[0xa6];
|
|
|
|
origNode = (buf[0xa9] << 8) + buf[0xa8];
|
|
|
|
origNet = (buf[0xad] << 8) + buf[0xac];
|
|
|
|
destNet = (buf[0xaf] << 8) + buf[0xae];
|
|
|
|
destZone = (buf[0xb1] << 8) + buf[0xb0];
|
|
|
|
origZone = (buf[0xb3] << 8) + buf[0xb2];
|
|
|
|
destPoint = (buf[0xb5] << 8) + buf[0xb4];
|
|
|
|
origPoint = (buf[0xb7] << 8) + buf[0xb6];
|
|
|
|
Attribute = (buf[0xbb] << 8) + buf[0xba];
|
|
|
|
|
|
|
|
Syslog('m', "From %d:%d/%d.%d to %d:%d/%d.%d", origZone, origNet, origNode, origPoint, destZone, destNet, destNode, destPoint);
|
|
|
|
|
2004-09-25 11:19:22 +00:00
|
|
|
while (Fgets(temp, PATH_MAX-1, fp)) {
|
|
|
|
Syslog('m', "Line \"%s\"", printable(temp, 0));
|
2004-07-24 15:32:30 +00:00
|
|
|
if (!strncmp(temp, "\001MSGID: ", 8)) {
|
|
|
|
msgid = xstrcpy(temp + 8);
|
|
|
|
/*
|
|
|
|
* Extra test to see if the mail comes from a pointaddress.
|
|
|
|
*/
|
|
|
|
l = strtok(temp," \0");
|
|
|
|
l = strtok(NULL," \0");
|
|
|
|
if ((ta = parsefnode(l))) {
|
|
|
|
if (ta->net == origNet && ta->node == origNode && !origPoint && ta->point) {
|
|
|
|
Syslog('m', "Setting pointinfo (%d) from MSGID", ta->point);
|
|
|
|
origPoint = ta->point;
|
|
|
|
}
|
|
|
|
if ((ta->net == origNet) && (ta->node == origNode) && (origZone == 0) && ta->zone) {
|
|
|
|
/*
|
|
|
|
* Missing zone info, maybe later we will see a INTL kludge or so, but for
|
|
|
|
* now, just in case we fix it. And we need that for some Aka collecting
|
|
|
|
* sysop who doesn't know how to configure his system right.
|
|
|
|
*/
|
|
|
|
Syslog('m', "No from zone set, setting zone %d from MSGID", ta->zone);
|
|
|
|
origZone = ta->zone;
|
|
|
|
/*
|
|
|
|
* 99.9 % chance that the destination zone is also missing.
|
|
|
|
*/
|
|
|
|
if (destZone == 0) {
|
|
|
|
destZone = ta->zone;
|
|
|
|
Syslog('m', "No dest zone set, setting zone %d from MSGID", ta->zone);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tidy_faddr(ta);
|
|
|
|
}
|
|
|
|
if (msgid)
|
|
|
|
free(msgid);
|
|
|
|
msgid = NULL;
|
|
|
|
}
|
|
|
|
if (!strncmp(temp, "\001FMPT", 5)) {
|
|
|
|
l = strtok(temp, " \0");
|
|
|
|
l = strtok(NULL, " \0");
|
|
|
|
origPoint = atoi(l);
|
|
|
|
}
|
|
|
|
if (!strncmp(temp, "\001TOPT", 5)) {
|
|
|
|
l = strtok(temp, " \0");
|
|
|
|
l = strtok(NULL, " \0");
|
|
|
|
destPoint = atoi(l);
|
|
|
|
}
|
|
|
|
if (!strncmp(temp, "\001INTL", 5)) {
|
|
|
|
Syslog('m', "Setting addresses from INTL kludge");
|
|
|
|
l = strtok(temp," \0");
|
|
|
|
l = strtok(NULL," \0");
|
|
|
|
r = strtok(NULL," \0");
|
|
|
|
if ((ta = parsefnode(l))) {
|
|
|
|
destPoint = ta->point;
|
|
|
|
destNode = ta->node;
|
|
|
|
destNet = ta->net;
|
|
|
|
destZone = ta->zone;
|
|
|
|
tidy_faddr(ta);
|
|
|
|
}
|
|
|
|
if ((ta = parsefnode(r))) {
|
|
|
|
origPoint = ta->point;
|
|
|
|
origNode = ta->node;
|
|
|
|
origNet = ta->net;
|
|
|
|
origZone = ta->zone;
|
|
|
|
tidy_faddr(ta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Check FLAGS kludge
|
|
|
|
*/
|
2007-08-25 18:32:07 +00:00
|
|
|
if (!strncmp((char *)buf, "\001FLAGS ", 7)) {
|
|
|
|
flagstr = xstrcpy((char *)buf + 7);
|
2004-07-24 15:32:30 +00:00
|
|
|
Syslog('m', "^aFLAGS %s", flagstr);
|
|
|
|
}
|
2007-08-25 18:32:07 +00:00
|
|
|
if (!strncmp((char *)buf, "\001FLAGS: ", 8)) {
|
|
|
|
flagstr = xstrcpy((char *)buf + 8);
|
2004-07-24 15:32:30 +00:00
|
|
|
Syslog('m', "^aFLAGS: %s", flagstr);
|
|
|
|
}
|
2005-02-03 20:23:29 +00:00
|
|
|
if (buf[0] != '\0') {
|
2007-08-25 18:32:07 +00:00
|
|
|
if ((buf[0] != '\001') && (strcmp((char *)buf, (char *)"--- ")))
|
2005-02-03 20:23:29 +00:00
|
|
|
empty = FALSE;
|
|
|
|
}
|
2004-07-24 15:32:30 +00:00
|
|
|
}
|
2005-02-03 20:23:29 +00:00
|
|
|
Syslog('m', "Mail is %sempty", empty ? "":"not ");
|
2004-07-24 15:32:30 +00:00
|
|
|
|
|
|
|
Syslog('m', "From %d:%d/%d.%d to %d:%d/%d.%d", origZone, origNet, origNode, origPoint, destZone, destNet, destNode, destPoint);
|
|
|
|
|
|
|
|
if ((origZone == 0) || (destZone == 0)) {
|
|
|
|
WriteError("No zone info in %s/%s", CFG.msgs_path, msgname);
|
|
|
|
fclose(fp);
|
|
|
|
free(temp);
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SearchFidonet(origZone) == FALSE) {
|
|
|
|
WriteError("Can't find network for zone %d", origZone);
|
|
|
|
fclose(fp);
|
|
|
|
free(temp);
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SearchNetBoard(origZone, origNet) == FALSE) {
|
|
|
|
WriteError("Can't find netmail board for zone:net %d:%d", origZone, origNet);
|
|
|
|
fclose(fp);
|
|
|
|
free(temp);
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Msg_Open(msgs.Base) && Msg_Lock(30L)) {
|
|
|
|
Msg_New();
|
|
|
|
strcpy(Msg.From, fromUserName);
|
|
|
|
strcpy(Msg.To, toUserName);
|
|
|
|
strcpy(Msg.Subject, subject);
|
|
|
|
if (Attribute & M_FILE) {
|
|
|
|
if ((stat(subject, &sb) == 0) && (S_ISREG(sb.st_mode))) {
|
|
|
|
dospath = xstrcpy(Unix2Dos(subject));
|
|
|
|
Syslog('+', "Fileattach %s in message %s", subject, msgname);
|
|
|
|
if (strlen(CFG.dospath))
|
|
|
|
strcpy(Msg.Subject, dospath);
|
|
|
|
Msg.FileAttach = TRUE;
|
|
|
|
free(dospath);
|
|
|
|
} else {
|
|
|
|
WriteError("Fileattach %s in message %s not found", subject, msgname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Msg.Written = parsefdate(DateTime, NULL);
|
|
|
|
Msg.Arrived = time(NULL) - (gmt_offset((time_t)0) * 60);
|
|
|
|
|
2008-11-26 22:12:28 +00:00
|
|
|
Msg.KillSent = ((Attribute & M_KILLSENT)) ? 1:0;
|
|
|
|
Msg.Hold = ((Attribute & M_HOLD)) ? 1:0;
|
2004-07-24 15:32:30 +00:00
|
|
|
Msg.Crash = ((Attribute & M_CRASH) || flag_on((char *)"CRA", flagstr));
|
|
|
|
Msg.ReceiptRequest = ((Attribute & M_RRQ) || flag_on((char *)"RRQ", flagstr));
|
|
|
|
Msg.Orphan = ((Attribute & M_ORPHAN));
|
|
|
|
Msg.Intransit = ((Attribute & M_TRANSIT));
|
|
|
|
Msg.FileRequest = ((Attribute & M_REQ) || flag_on((char *)"FRQ", flagstr));
|
|
|
|
Msg.ConfirmRequest = ((Attribute & M_AUDIT) || flag_on((char *)"CFM", flagstr));
|
|
|
|
Msg.Immediate = flag_on((char *)"IMM", flagstr);
|
|
|
|
Msg.Direct = flag_on((char *)"DIR", flagstr);
|
|
|
|
Msg.Gate = flag_on((char *)"ZON", flagstr);
|
|
|
|
|
2004-07-24 19:18:11 +00:00
|
|
|
if ((origZone == destZone) && (origNet == destNet) && (origNode == destNode) && (origPoint == destPoint)) {
|
|
|
|
/*
|
|
|
|
* Message is local, make the message appear as a received netmail
|
|
|
|
*/
|
2005-02-03 20:23:29 +00:00
|
|
|
if (empty) {
|
|
|
|
Syslog('+', "Empty local netmail for %s dropped", toUserName);
|
|
|
|
Msg_UnLock();
|
|
|
|
Msg_Close();
|
|
|
|
fclose(fp);
|
|
|
|
free(temp);
|
|
|
|
return 6;
|
|
|
|
}
|
2004-07-24 19:18:11 +00:00
|
|
|
islocal = TRUE;
|
|
|
|
if ((strncasecmp(toUserName, "sysop", 5) == 0) ||
|
|
|
|
(strncasecmp(toUserName, "postmaster", 10) == 0) ||
|
|
|
|
(strncasecmp(toUserName, "coordinator", 11) == 0)) {
|
|
|
|
Syslog('+', " Readdress from %s to %s", toUserName, CFG.sysop_name);
|
2005-08-28 14:10:06 +00:00
|
|
|
snprintf(toUserName, 37, "%s", CFG.sysop_name);
|
2004-07-24 19:18:11 +00:00
|
|
|
strcpy(Msg.To, toUserName);
|
|
|
|
}
|
2004-07-24 19:30:26 +00:00
|
|
|
net_imp++;
|
2004-07-24 19:18:11 +00:00
|
|
|
} else {
|
|
|
|
Msg.Local = TRUE;
|
|
|
|
islocal = FALSE;
|
|
|
|
}
|
|
|
|
Syslog('m', "Netmail is %s", islocal ? "Local":"for export");
|
2004-07-24 15:32:30 +00:00
|
|
|
Msg.Private = TRUE;
|
2004-07-25 18:55:53 +00:00
|
|
|
Msg.Netmail = TRUE;
|
2004-07-24 15:32:30 +00:00
|
|
|
|
|
|
|
if (origPoint)
|
2005-08-28 14:10:06 +00:00
|
|
|
snprintf(Msg.FromAddress, 101, "%d:%d/%d.%d@%s", origZone, origNet, origNode, origPoint, fidonet.domain);
|
2004-07-24 15:32:30 +00:00
|
|
|
else
|
2005-08-28 14:10:06 +00:00
|
|
|
snprintf(Msg.FromAddress, 101, "%d:%d/%d@%s", origZone, origNet, origNode, fidonet.domain);
|
2004-07-24 15:32:30 +00:00
|
|
|
if (SearchFidonet(destZone)) {
|
|
|
|
if (destPoint)
|
2005-08-28 14:10:06 +00:00
|
|
|
snprintf(Msg.ToAddress, 101, "%d:%d/%d.%d@%s", destZone, destNet, destNode, destPoint, fidonet.domain);
|
2004-07-24 15:32:30 +00:00
|
|
|
else
|
2005-08-28 14:10:06 +00:00
|
|
|
snprintf(Msg.ToAddress, 101, "%d:%d/%d@%s", destZone, destNet, destNode, fidonet.domain);
|
2004-07-24 15:32:30 +00:00
|
|
|
} else {
|
|
|
|
if (destPoint)
|
2005-08-28 14:10:06 +00:00
|
|
|
snprintf(Msg.ToAddress, 101, "%d:%d/%d.%d", destZone, destNet, destNode, destPoint);
|
2004-07-24 15:32:30 +00:00
|
|
|
else
|
2005-08-28 14:10:06 +00:00
|
|
|
snprintf(Msg.ToAddress, 101, "%d:%d/%d", destZone, destNet, destNode);
|
2004-07-24 15:32:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add original message text
|
|
|
|
*/
|
|
|
|
fseek(fp, 0xbe, SEEK_SET);
|
|
|
|
while (fgets(temp, PATH_MAX-1, fp)) {
|
|
|
|
Striplf(temp);
|
|
|
|
if (temp[strlen(temp)-1] == '\r')
|
|
|
|
temp[strlen(temp)-1] = '\0';
|
|
|
|
MsgText_Add2(temp);
|
|
|
|
}
|
|
|
|
|
|
|
|
Msg_AddMsg();
|
|
|
|
Msg_UnLock();
|
|
|
|
Syslog('+', "%s => %s (%ld) to \"%s\", \"%s\"", msgname, msgs.Name, Msg.Id, Msg.To, Msg.Subject);
|
|
|
|
|
|
|
|
msgs.LastPosted = time(NULL);
|
|
|
|
msgs.Posted.total++;
|
|
|
|
msgs.Posted.tweek++;
|
|
|
|
msgs.Posted.tdow[Diw]++;
|
|
|
|
msgs.Posted.month[Miy]++;
|
|
|
|
UpdateMsgs();
|
|
|
|
|
2004-07-24 19:18:11 +00:00
|
|
|
if (!islocal) {
|
|
|
|
do_scan = TRUE;
|
2005-08-28 14:10:06 +00:00
|
|
|
snprintf(temp, PATH_MAX, "%s/tmp/netmail.jam", getenv("MBSE_ROOT"));
|
2004-07-24 19:18:11 +00:00
|
|
|
if ((np = fopen(temp, "a")) != NULL) {
|
2005-10-11 20:49:41 +00:00
|
|
|
fprintf(np, "%s %u\n", msgs.Base, Msg.Id);
|
2004-07-24 19:18:11 +00:00
|
|
|
fclose(np);
|
|
|
|
}
|
2004-07-24 15:32:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Msg_Close();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
WriteError("Can't open JAM %s", msgs.Base);
|
|
|
|
rc = 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
if (rc == 0) {
|
2004-07-30 18:49:25 +00:00
|
|
|
net_in++;
|
2005-08-28 14:10:06 +00:00
|
|
|
snprintf(temp, PATH_MAX, "%s/%s", CFG.msgs_path, msgname);
|
2004-07-30 18:49:25 +00:00
|
|
|
if (unlink(temp) != 0)
|
|
|
|
WriteError("Can't remove %s", temp);
|
2004-07-24 15:32:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
free(temp);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|