コード例 #1
0
def cb_post(url, jdata):
    log.info(">>>> [callback] POSTing to {}: {}".format(url, jdata))
    rq = requests.post(url,
                       headers={'User-Agent': USER_AGENT},
                       json=json.loads(jdata))
    if rq.status_code >= 400:
        log.warning(
            ">>>> [callback] POSTing to {} failed with status {}: {}".format(
                url, rq.status_code, rq.text))
コード例 #2
0
ファイル: mm4rx.py プロジェクト: drivefast/mmsgw
 def _process(self, ev):
     # change name of the file asap, to minimize the probability of racing conditions 
     # when processing across multiple instances of the app
     if ev.name.startswith("_"): return
     fn = self.spool_dir + "_" + ev.name
     try:
         shutil.move(ev.pathname, fn)
     except Exception as e:
         log.warning(">>>> possible MM4 file watcher racing condition: " + str(e))
         return
     # parse the file content to get the from and to addresses
     try:
         with open(fn, "rb") as fh:
             msg = email.message_from_binary_file(fh)
             dispatch(fh.read(),
                 msg.get('from'), 
                 email.utils.getaddresses(msg.get_all('to')) 
             )
     except email.errors.MessageParseError as me:
         log.warning(">>>> MM4 file watcher failed to parse {}: {}"
             .format(spool_fn, me)
         )
     except Exception as e:
         log.debug(traceback.format_exc())
         log.warning(">>>> MM4 file watcher failed: {}".format(e))
コード例 #3
0
ファイル: mm4rx.py プロジェクト: drivefast/mmsgw
def dispatch(content, sender, receivers, source=None):

    log.info(">>>> {} inbound on MM4 interface - From: {}, To: {}, length: {}"
        .format(source or "", sender, receivers, len(content))
    )
    log.debug(">>>> content: {}{}"
        .format(content[:4096], ("..." if len(content) > 4096 else ""))
    )
    if len(content) > 4096:
        log.debug(">>>> ...{}".format(content[-256:]))

    # get a gateway that can handle the message; preference is to search 
    # by receiver address first, by sending host next, and by sender address last
    gw = \
        cfg['receivers'].get(email.utils.parseaddr(receivers[0])[1]) or \
        cfg['peers'].get(source[0]) if source is not None else None or \
        cfg['senders'].get(email.utils.parseaddr(sender)[1])
    if gw is None:
        log.warning(">>>> no gateway to process this email")
        return "555 MAIL FROM/RCPT TO parameters not recognized"

    mm4rx_id = str(uuid.uuid4()).replace("-", "")
    
    # move content as file to be processed
    fn = repo(TMP_MMS_DIR, mm4rx_id + ".mm4")
    if cfg['general'].get('smtp_host'):
        with open(fn, "wb") as fh:
            fh.write(content)

    # post a task for the gateway parser
    q_rx = rq.Queue("QRX-" + gw, connection=rdbq)
    q_rx.enqueue_call(
        func='models.gateway.inbound', args=( mm4rx_id + ".mm4", ),
        job_id=mm4rx_id,
        meta={ 'retries': MAX_GW_RETRIES },
        ttl=30
    )
    log.info(">>>> message {}, queued for processing by gateway {}".format(mm4rx_id, gw))

    return None
コード例 #4
0
ファイル: mm4rx.py プロジェクト: drivefast/mmsgw
if len(sys.argv) < 2:
    print("To start the MM4 mail utility, use a configuration filename as a command line argument.\n")
    exit()
cfg = configparser.ConfigParser()
cfg.read(sys.argv[len(sys.argv) - 1])

TMP_MMS_DIR = cfg['general'].get('tmp_dir', "/tmp/mms/")
if not TMP_MMS_DIR.endswith("/"):
    TMP_MMS_DIR += "/"

bind_host = cfg['general'].get('smtp_host', '')
bind_port = int(cfg['general'].get('smtp_port', 25))
if bind_host:
    _1 = MM4SMTPServer(( bind_host, bind_port ), None)
    log.warning(">>>> MM4 SMTP daemon started, listening on {}:{}".format(bind_host, bind_port))

spool = cfg['general'].get('spool_dir')
if spool:
    wm = pyinotify.WatchManager()
    h = MaildirEventHandler()
    h.spool_dir = spool
    notifier = pyinotify.AsyncNotifier(wm, h)
    _2 = wm.add_watch(spool, 
        pyinotify.IN_CLOSE_WRITE | pyinotify.IN_MOVED_TO
#        exclude_filter=pyinotify.ExcludeFilter([ spool + "_*" ])
    )
    log.warning(">>>> MM4 file daemon started, watching " + spool)

asyncore.loop()
コード例 #5
0
ファイル: message.py プロジェクト: drivefast/mmsgw
def mm7_inbound(gw):
    log.info("[{}] request received, {} bytes".format(
        gw, bottle.request.headers['Content-Length']))
    raw_content = bottle.request.body.read().decode()
    log.debug("[{}] request headers: {}".format(gw, [
        "{}: {}".format(h, bottle.request.headers.get(h))
        for h in bottle.request.headers.keys()
    ]))
    log.debug("[{}] >>>> raw content: {}...".format(gw, raw_content[:4096]))
    if len(raw_content) > 4096:
        log.debug("[{}] >>>> ... {}".format(gw, raw_content[-256:]))

    bottle.response.content_type = "text/xml"
    mime_headers = "Mime-Version: 1.0\nContent-Type: " + bottle.request.headers[
        'Content-Type']

    # try parsing lightly, to determine what queue to place this in
    m = email.message_from_string(mime_headers + "\n\n" + raw_content)
    try:
        if m.is_multipart():
            parts = m.get_payload()
            log.debug("[{}] handling as multipart, {} parts".format(
                gw, len(parts)))
            env_content = parts[0].get_payload(decode=True).decode()
            log.debug("[{}] SOAP envelope: {}".format(gw, env_content))
            env = ET.fromstring(env_content)
        else:
            log.debug("[{}] handling as single part".format(gw))
            env = ET.fromstring(m.get_payload(decode=True))
    except ET.ParseError as e:
        log.warning("[{}] Failed to xml-parse the SOAP envelope: {}".format(
            gw, e))
        return bottle.HTTPResponse(
            status=400, body="Failed to xml-parse the SOAP envelope")

    # get the transaction tag, treat it as unique ID of the incoming message
    env_ns = "{" + MM7_NAMESPACE['env'] + "}"
    transaction_id = None
    t_header = env.find("./" + env_ns + "Header") or []
    for t in t_header:
        if t.tag.endswith("TransactionID"):
            transaction_id = t.text.strip()
            mm7_ns = t.tag.replace("TransactionID", "")
            break
    if transaction_id is None:
        s = "SOAP envelope of received request invalid, at least missing a transaction ID"
        log.warning("[{}] {}".format(gw, s))
        return bottle.HTTPResponse(status=400, body=s)

    # try to identify the message type
    mo_meta = \
        env.find("./" + env_ns + "Body/" + mm7_ns + "DeliverReq") or \
        env.find("./" + env_ns + "Body/" + mm7_ns + "SubmitReq")
    if mo_meta:
        # create a shallow message to send back to the MMSC
        rx = MMSMessage()
        rx.last_tran_id = transaction_id
        rx.direction = 1
        log.debug("[{}] {} Incoming message {} is an MO".format(
            gw, rx.id, transaction_id))
        rx.save()
        rx.template.save()
        # save raw content
        fn = repo(TMP_MMS_DIR, rx.id + ".mm7")
        log.debug("[{}] {} saving media as {}".format(gw, rx.id,
                                                      transaction_id, fn))
        with open(fn, "w") as fh:
            fh.write(parts[1].as_string())
        # schedule message for processing
        q_rx = rq.Queue("QRX-" + gw, connection=rdbq)
        q_rx.enqueue_call(func='models.gateway.inbound',
                          args=(
                              rx.id + ".mm7",
                              ET.tostring(mo_meta),
                          ),
                          job_id=rx.id,
                          meta={'retries': MAX_GW_RETRIES},
                          ttl=30)
        # send MM7 response
        return models.gateway.MM7Gateway.build_response(
            "DeliverRsp", transaction_id, rx.id, '1000')

    m_meta = \
        env.find("./" + env_ns + "Body/" + mm7_ns + "DeliveryReportReq") or \
        env.find("./" + env_ns + "Body/" + mm7_ns + "ReadReplyReq")
    if m_meta:
        m_type = m_meta.tag.replace(mm7_ns, "")
        m_reply_type = "DeliveryReportRsp" if m_meta.tag.replace(
            mm7_ns, "") else "ReadReplyRsp"
        log.debug("[{}] Incoming message is a {}".format(gw, m_type))
        provider_msg_id = m_meta.findtext("./" + mm7_ns + "MessageID", "")
        if provider_msg_id is None:
            log.warning("[{}] No MessageID tag found in the {}".format(
                gw, m_meta))
            return models.gateway.MM7Gateway.build_response(
                m_reply_type, transaction_id, "", '4004')
        # find MT message
        txid = rdb.get('mmsxref-' + provider_msg_id)
        if txid is None:
            log.info(
                "[{}] Couldnt find reference to original message for MessageID {}"
                .format(gw, provider_msg_id))
            return models.gateway.MM7Gateway.build_response(
                m_reply_type, transaction_id, provider_msg_id, '2005')
        tx = MMSMessage(txid)
        if tx.id is None:
            log.info(
                "[{}] {} Couldnt find original message record for MessageID {}"
                .format(gw, provider_msg_id))
            return models.gateway.MM7Gateway.build_response(
                m_reply_type, transaction_id, provider_msg_id, '2005')
        # schedule DR for processing
        q_rx = rq.Queue("QRX-" + gw, connection=rdbq)
        q_rx.enqueue_call(func='models.gateway.inbound',
                          args=(
                              "",
                              ET.tostring(m_meta),
                          ),
                          job_id=transaction_id,
                          meta={'retries': MAX_GW_RETRIES},
                          ttl=30)
        # send MM7 response
        return models.gateway.MM7Gateway.build_response(
            m_reply_type, transaction_id, provider_msg_id, '1000')

    # handling for other MM7 requests (cancel, replace, etc) go here

    log.warning("[{}] Unknown or unhandled message type".format(gw))
    return bottle.HTTPResponse(status=400,
                               body="Unknown or unhandled message type")