summaryrefslogtreecommitdiffstats
path: root/pttbbs/daemon/commentd/commentd.py
blob: 1c3a79d63546e8c99fb762ebdc45169743c8cee0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env python

import struct
import collections
import sys
import logging
import os

from gevent import monkey; monkey.patch_all()
import gevent
import gevent.server
import leveldb

# Ref: ../../include/pttstruct.h
IDLEN = 12
FNLEN = 28
COMMENTLEN = 80


# Ref: ../../include/daemons.h
RequestFormatString = 'HH';
Request = collections.namedtuple('Request', 'cb operation')
CommentFormatString = "IIII%ds%ds" % (IDLEN + 1, COMMENTLEN + 1)
Comment = collections.namedtuple('Comment',
    'time ipv4 userref type userid msg')
CommentKeyFormatString = '%ds%ds' % (IDLEN + 1, FNLEN + 1)
CommentKey = collections.namedtuple('CommentKey', 'board file')
REQ_ADD = 1
REQ_QUERY = 2
_SERVER_ADDR = '127.0.0.1'
_SERVER_PORT = 5134
_DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)),
            'db_comments.db')


def UnpackComment(blob):
    def strip_if_string(v):
    return v.strip(chr(0)) if type(v) == str else v
    data = struct.unpack(CommentFormatString, blob)
    logging.debug("UnpackComment: %r" % (data,))
    return Comment._make(map(strip_if_string, data))

def UnpackCommentKey(blob):
    def strip_if_string(v):
    return v.strip(chr(0)) if type(v) == str else v
    data = struct.unpack(CommentKeyFormatString, blob)
    logging.debug("UnpackCommentKey: %r" % (data,))
    return CommentKey._make(map(strip_if_string, data))

def PackComment(comment):
    return struct.pack(CommentFormatString, *comment)

def LoadComment(key):
    blob = g_db.get(key)
    if blob is None:
    return ''
    else:
    return UnpackComment(g_db.get(key))

def SaveComment(keypak, comment):
    logging.debug("SaveComment: %r => %r" % (keypak, comment))
    key = '%s/%s' % (keypak.board, keypak.file)
    num = g_db.get(key)
    num = 1 if (num is None) else (int(num) + 1)
    g_db.set(key, '%d' % num)
    key += '#%08d' % (num)
    g_db.set(key, PackComment(comment))
    logging.info('Saved comment: %s', key)

def open_database(db_path):
    global g_db

    class LevelDBWrapper(object):
    def __init__(self, db):
        self.db = db

    def get(self, key):
        try:
        return self.db.Get(key)
        except KeyError:
        return None

    def set(self, key, value):
        self.db.Put(key, value)

    g_db = LevelDBWrapper(leveldb.LevelDB(db_path))
    return g_db

def handle_request(socket, _):
    fd = socket.makefile('rw')
    fmt_len = '@i'
    try:
        req = fd.read(struct.calcsize(RequestFormatString))
    req = Request._make(struct.unpack(RequestFormatString, req))
    logging.debug('Found request: %d' % req.operation)
        # TODO check req.cb
    if req.operation == REQ_ADD:
        blob = fd.read(struct.calcsize(CommentFormatString))
        keyblob = fd.read(struct.calcsize(CommentKeyFormatString))
        SaveComment(UnpackCommentKey(keyblob), UnpackComment(blob))
    elif req.operation == REQ_QUERY:
        raise NotImplementedError('REQ_QUERY is not implemented')
    else:
        raise ValueError('Unkown operation %d' % req.operation)
    except:
        logging.exception("handle_request")
    finally:
        try:
            fd.close()
            socket.close()
        except:
            pass

def main(myname, argv):
    level = logging.INFO
    level = logging.WARNING
    level = logging.DEBUG
    logging.basicConfig(level=level, format='%(asctime)-15s %(message)s')
    if len(argv) not in [0, 1]:
        print "Usage: %s [db_path]" % myname
        exit(1)
    db_path = argv[0] if len(argv) > 0 else _DB_PATH
    logging.warn("Serving at %s:%s [db:%s][pid:%d]...",
                 _SERVER_ADDR, _SERVER_PORT, db_path, os.getpid())
    open_database(db_path)
    server = gevent.server.StreamServer(
        (_SERVER_ADDR, _SERVER_PORT), handle_request)
    server.serve_forever()

if __name__ == '__main__':
    main(sys.argv[0], sys.argv[1:])