summaryrefslogtreecommitdiffstats
path: root/content/includes/xmltools.js
blob: 68b69d0b31af4b82c79ac089db5ab566d768d423 (plain)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * This file is part of EAS-4-TbSync.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
 */
 
 "use strict";

var xmltools = {

    isString : function (obj) {
        return (Object.prototype.toString.call(obj) === '[object String]');
    },
        
    checkString : function(d, fallback = "") {
        if (this.isString(d)) return d;
        else return fallback;
    },	    
        
    nodeAsArray : function (node) {
        let a = [];
        if (node) {
            //return, if already an array
            if (node instanceof Array) return node;

            //else push node into an array
            a.push(node);
        }
        return a;
    },

    hasWbxmlDataField: function(wbxmlData, path) {
        if (wbxmlData) {		
            let pathElements = path.split(".");
            let data = wbxmlData;
            for (let x = 0; x < pathElements.length; x++) {
                if (data[pathElements[x]]) data = data[pathElements[x]];
                else return false;
            }
            return true;
        }
        return false;
    },

    getWbxmlDataField: function(wbxmlData,path) {
        if (wbxmlData) {		
            let pathElements = path.split(".");
            let data = wbxmlData;
            let valid = true;
            for (let x = 0; valid && x < pathElements.length; x++) {
                if (data[pathElements[x]]) data = data[pathElements[x]];
                else valid = false;
            }
            if (valid) return data;
        }
        return false
    },

    //print content of xml data object (if debug output enabled)
    printXmlData : function (data, printApplicationData) {
        if (TbSync.prefs.getIntPref("log.userdatalevel") > 1 || (TbSync.prefs.getIntPref("log.userdatalevel") == 1 && printApplicationData)) {
            let dump = JSON.stringify(data);
            TbSync.dump("Extracted XML data", "\n" + dump);
        }
    },

    getDataFromXMLString: function (str) {
        let data = null;
        let xml = "";        
        if (str == "") return data;
        
        let oParser = (Services.vc.compare(Services.appinfo.platformVersion, "61.*") >= 0) ? new DOMParser() : Components.classes["@mozilla.org/xmlextras/domparser;1"].createInstance(Components.interfaces.nsIDOMParser);
        try {
            xml = oParser.parseFromString(str, "application/xml");
        } catch (e) {
            //however, domparser does not throw an error, it returns an error document
            //https://developer.mozilla.org/de/docs/Web/API/DOMParser
            //just in case
            throw eas.sync.finish("error", "malformed-xml");
        }

        //check if xml is error document
        if (xml.documentElement.nodeName == "parsererror") {
            TbSync.dump("BAD XML", "The above XML and WBXML could not be parsed correctly, something is wrong.");
            throw eas.sync.finish("error", "malformed-xml");
        }

        try {
            data = this.getDataFromXML(xml);
        } catch (e) {
            throw eas.sync.finish("error", "mailformed-data");
        }
        
        return data;
    },
    
    //create data object from XML node
    getDataFromXML : function (nodes) {
        
        /*
         * The passed nodes value could be an entire document in a single node (type 9) or a 
         * single element node (type 1) as returned by getElementById. It could however also 
         * be an array of nodes as returned by getElementsByTagName or a nodeList as returned
         * by childNodes. In that case node.length is defined.
         */        
        
        // create the return object
        let obj = {};
        let nodeList = [];
        let multiplicity = {};
        
        if (nodes.length === undefined) nodeList.push(nodes);
        else nodeList = nodes;
        
        // nodelist contains all childs, if two childs have the same name, we cannot add the chils as an object, but as an array of objects
        for (let node of nodeList) { 
            if (node.nodeType == 1 || node.nodeType == 3) {
                if (!multiplicity.hasOwnProperty(node.nodeName)) multiplicity[node.nodeName] = 0;
                multiplicity[node.nodeName]++;
                //if this nodeName has multiplicity > 1, prepare obj  (but only once)
                if (multiplicity[node.nodeName]==2) obj[node.nodeName] = [];
            }
        }

        // process nodes
        for (let node of nodeList) { 
            switch (node.nodeType) {
                case 9: 
                    //document node, dive directly and process all children
                    if (node.hasChildNodes) obj = this.getDataFromXML(node.childNodes);
                    break;
                case 1: 
                    //element node
                    if (node.hasChildNodes) {
                        //if this is an element with only one text child, do not dive, but get text childs value
                        let o;
                        if (node.childNodes.length == 1 && node.childNodes.item(0).nodeType==3) {
                            //the passed xml is a save xml with all special chars in the user data encoded by encodeURIComponent
                            o = decodeURIComponent(node.childNodes.item(0).nodeValue);
                        } else {
                            o = this.getDataFromXML(node.childNodes);
                        }
                        //check, if we can add the object directly, or if we have to push it into an array
                        if (multiplicity[node.nodeName]>1) obj[node.nodeName].push(o)
                        else obj[node.nodeName] = o; 
                    }
                    break;
            }
        }
        return obj;
    }
    
};