Esempio n. 1
0
    def __call__(self, environ, start_response):
        self.environ = environ
        self.start = start_response
        self.request = Request(environ)
        self.response = Response()

        l = self.request.headers.get('Content-Length', '0')
        if l == '':
            l = '0'
        l = int(l)
        data = self.request.body

        # we assume it's LLSD for now and try to parse it
        # TODO: check headers
        try:
            data = llsd.parse(data)
            if data is False:  # might happen with GET
                data = {}
        except:
            self.response.status = 500
            return self.response(self.environ, self.start)

        if self.request.path == "/network/get" and self.request.method == "GET":
            self.response.status = 200
            self.response.body = "Hello, World"
            return self.response(self.environ, self.start)
        elif self.request.path == "/network/post" and self.request.method == "POST":
            data = self.request.body
            self.response.status = 200
            self.response.body = "returned: %s" % data
            return self.response(self.environ, self.start)
        else:
            return self.send_response(404, 'resource not found.')
Esempio n. 2
0
    def deserialize_string(self, data):
        """ deserialize a string """

        try:
            r = llsd.parse(data)
        except llsd.LLSDParseError, e:
            raise DeserializationFailed(data, str(e))
Esempio n. 3
0
    def __call__(self, environ, start_response):
        self.environ = environ
        self.start = start_response
        self.request = Request(environ)
        self.response = Response()

        l = self.request.headers.get('Content-Length', '0')
        if l == '':
            l = '0'
        l = int(l)
        data = self.request.body

        # we assume it's LLSD for now and try to parse it
        # TODO: check headers
        try:
            data = llsd.parse(data)
            if data is False:  # might happen with GET
                data = {}
        except:
            self.response.status = 500
            return self.response(self.environ, self.start)

        if self.request.path == "/auth.cgi":
            return self.handle_ogp_login(data)
        else:
            return self.send_response(404, 'resource not found.')
Esempio n. 4
0
 def _try_cmd(self, args):
     """
     Try running an edit 'command with args'.
     Return results.
     """
     self.edit_cmd.main(args)
     return llsd.parse(file(self.tmp_file, 'rb').read())
Esempio n. 5
0
 def _try_cmd(self, args):
     """
     Try running an edit 'command with args'.
     Return results.
     """
     self.edit_cmd.main(args)
     return llsd.parse(file(self.tmp_file, 'rb').read())
def load_vsvars(vsver):
    vsvars_path = os.path.join(os.environ["VS%sCOMNTOOLS" % vsver], "vsvars32.bat")
    temp_script_name = tempfile.mktemp(suffix=".cmd")

    shutil.copy(vsvars_path, temp_script_name)
    # append our little llsd+notation bit to the end
    temp_script_file = open(temp_script_name, "a")
    temp_script_file.write("""
        echo {
        echo "VSPATH":"%PATH%",
        echo "VSINCLUDE":"%INCLUDE%",
        echo "VSLIB":"%LIB%",
        echo "VSLIBPATH":"%LIBPATH%",
        echo }
    """)
    temp_script_file.close()

    if sys.platform == "cygwin":
        # cygwin needs the file to have executable permissions, and translated to a path
        # name that cmd.exe can understand
        cmdline = ["cygpath", "-d", "%s" % temp_script_name]
        logger.debug(cmdline)
        os.chmod(temp_script_name, stat.S_IREAD|stat.S_IWRITE|stat.S_IEXEC)
        (temp_script_name, _) = subprocess.Popen(cmdline, stdout=subprocess.PIPE).communicate()
        temp_script_name = temp_script_name.rstrip()

    cmdline = ['cmd', '/Q', '/C', temp_script_name]
    logger.debug(cmdline)
    cmd = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
    (cmdout, cmderr) = cmd.communicate()

    logger.debug("cmdout: %r" % cmdout)

    os.remove(temp_script_name)

    # *HACK
    # slice off the 1st line ("Setting environment for using..." preamble)
    cmdout = '\n'.join(cmdout.split('\n')[1:])
    # escape backslashes
    cmdout = '\\\\'.join(cmdout.split('\\'))

    vsvars = llsd.parse(cmdout)

    logger.debug("VSVARS: %r" % vsvars)

    # translate paths from windows to cygwin format
    vsvars['VSPATH'] = ":".join(
        ['"$(cygpath -u \'%s\')"' % p for p in vsvars['VSPATH'].split(';') ]
    )

    # fix for broken builds on windows (don't let anything escape the closing quote)
    for (k,v) in vsvars.iteritems():
        vsvars[k] = v.rstrip('\\')

    return vsvars
    def test__decode_eq_results3(self):

        llsd_data = '<llsd><map><key>events</key><array><map><key>body</key><map><key>session_id</key><uuid>6e20d408-2702-ea83-04b4-12cef089a327</uuid><key>session_info</key><map><key>moderated_mode</key><map><key>voice</key><boolean>0</boolean></map><key>session_name</key><string>Pyogp</string><key>type</key><integer>0</integer><key>voice_enabled</key><boolean>1</boolean></map><key>success</key><boolean>1</boolean><key>temp_session_id</key><uuid>6e20d408-2702-ea83-04b4-12cef089a327</uuid></map><key>message</key><string>ChatterBoxSessionStartReply</string></map><map><key>body</key><map><key>agent_updates</key><map><key>43b8b2d7-99b0-4b60-a935-45896c74be62</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map><key>4ca88f33-f34a-4ae4-a8f1-7357f883789e</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map><key>77e48a07-06f1-4ae4-9d33-6eab4d445e2d</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map><key>79e7c4ad-3361-4736-bced-1f72e6c3dbd4</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map><key>7a59bbd8-5175-4cff-8328-f92b3acde98a</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map><key>94e1350b-64d6-444b-8d3c-d1da460b259c</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map><key>97980bee-d865-4100-80e7-9763e53e8a6a</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map><key>a1f21e11-7db3-4eb4-89f3-5a65eea34495</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map><key>a517168d-1af5-4854-ba6d-672c8a59e439</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map><key>b2161990-f5c4-4dcb-a73c-e7365a18adfd</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map><key>c12cce9d-3308-4517-9a5e-1f3460187e56</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map><key>eeaedeb6-c702-472a-b725-e4492095c69a</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map><key>transition</key><string>ENTER</string></map></map><key>session_id</key><string>6e20d408-2702-ea83-04b4-12cef089a327</string><key>updates</key><map><key>43b8b2d7-99b0-4b60-a935-45896c74be62</key><string>ENTER</string><key>4ca88f33-f34a-4ae4-a8f1-7357f883789e</key><string>ENTER</string><key>77e48a07-06f1-4ae4-9d33-6eab4d445e2d</key><string>ENTER</string><key>79e7c4ad-3361-4736-bced-1f72e6c3dbd4</key><string>ENTER</string><key>7a59bbd8-5175-4cff-8328-f92b3acde98a</key><string>ENTER</string><key>94e1350b-64d6-444b-8d3c-d1da460b259c</key><string>ENTER</string><key>97980bee-d865-4100-80e7-9763e53e8a6a</key><string>ENTER</string><key>a1f21e11-7db3-4eb4-89f3-5a65eea34495</key><string>ENTER</string><key>a517168d-1af5-4854-ba6d-672c8a59e439</key><string>ENTER</string><key>b2161990-f5c4-4dcb-a73c-e7365a18adfd</key><string>ENTER</string><key>c12cce9d-3308-4517-9a5e-1f3460187e56</key><string>ENTER</string><key>eeaedeb6-c702-472a-b725-e4492095c69a</key><string>ENTER</string></map></map><key>message</key><string>ChatterBoxSessionAgentListUpdates</string></map><map><key>body</key><map><key>agent_updates</key><map><key>a517168d-1af5-4854-ba6d-672c8a59e439</key><map><key>info</key><map><key>can_voice_chat</key><boolean>1</boolean><key>is_moderator</key><boolean>0</boolean></map></map></map><key>session_id</key><string>6e20d408-2702-ea83-04b4-12cef089a327</string><key>updates</key><map /></map><key>message</key><string>ChatterBoxSessionAgentListUpdates</string></map><map><key>body</key><map><key>SimulatorInfo</key><array><map><key>Handle</key><binary encoding="base64">AAP8AAADxAA=</binary><key>IP</key><binary encoding="base64">2FIT1A==</binary><key>Port</key><integer>12035</integer></map></array></map><key>message</key><string>EnableSimulator</string></map></array><key>id</key><integer>17</integer></map></llsd>'

        data = llsd.parse(llsd_data)

        packets = self.eq._decode_eq_result(data)

        packet_names = []

        for packet in packets:
            self.assertEquals(str(type(packet)), '<class \'pyogp.lib.base.message.message.Message\'>')
            packet_names.append(packet.name)    
Esempio n. 8
0
    def __init__(self, message_xml=None):
        """ parse message.xml and store a representation of the map """

        if not message_xml:
            self.raw_llsd = msg_details
        else:
            self.raw_llsd = message_xml

        self.parsed_llsd = llsd.parse(self.raw_llsd)

        self.serverDefaults = self.parsed_llsd['serverDefaults']
        self.messages = self.parsed_llsd['messages']
        self.capBans = self.parsed_llsd['capBans']
        self.maxQueuedEvents = self.parsed_llsd['maxQueuedEvents']
        self.messageBans = self.parsed_llsd['messageBans']
    def test__decode_eq_result2(self):

        llsd_data = '<llsd><map><key>events</key><array><map><key>body</key><map><key>AgentData</key><array><map><key>AgentID</key><uuid>a517168d-1af5-4854-ba6d-672c8a59e439</uuid></map></array><key>GroupData</key><array><map><key>AcceptNotices</key><boolean>1</boolean><key>Contribution</key><integer>0</integer><key>GroupID</key><uuid>4dd70b7f-8b3a-eef9-fc2f-909151d521f6</uuid><key>GroupInsigniaID</key><uuid /><key>GroupName</key><string>Enus&apos; Construction Crew</string><key>GroupPowers</key><binary encoding="base64">AAA5ABgBAAA=</binary><key>ListInProfile</key><boolean>0</boolean></map><map><key>AcceptNotices</key><boolean>1</boolean><key>Contribution</key><integer>0</integer><key>GroupID</key><uuid>69fd708c-3f20-a01b-f9b5-b5c4b310e5ca</uuid><key>GroupInsigniaID</key><uuid /><key>GroupName</key><string>EnusBot Army</string><key>GroupPowers</key><binary encoding="base64">AAD5ABgBAAA=</binary><key>ListInProfile</key><boolean>0</boolean></map></array></map><key>message</key><string>AgentGroupDataUpdate</string></map><map><key>body</key><map><key>AgeVerificationBlock</key><array><map><key>RegionDenyAgeUnverified</key><boolean>0</boolean></map></array><key>MediaData</key><array><map><key>MediaDesc</key><string /><key>MediaHeight</key><integer>0</integer><key>MediaLoop</key><integer>1</integer><key>MediaType</key><string>none/none</string><key>MediaWidth</key><integer>0</integer><key>ObscureMedia</key><integer>1</integer><key>ObscureMusic</key><integer>1</integer></map></array><key>ParcelData</key><array><map><key>AABBMax</key><array><real>256</real><real>256</real><real>50</real></array><key>AABBMin</key><array><real>0</real><real>0</real><real>0</real></array><key>Area</key><integer>65536</integer><key>AuctionID</key><binary encoding="base64">AAAAAA==</binary><key>AuthBuyerID</key><uuid /><key>Bitmap</key><binary encoding="base64">//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8=</binary><key>Category</key><integer>255</integer><key>ClaimDate</key><integer>1088625472</integer><key>ClaimPrice</key><integer>0</integer><key>Desc</key><string /><key>GroupID</key><uuid /><key>GroupPrims</key><integer>0</integer><key>IsGroupOwned</key><boolean>0</boolean><key>LandingType</key><integer>1</integer><key>LocalID</key><integer>15</integer><key>MaxPrims</key><integer>3750</integer><key>MediaAutoScale</key><integer>0</integer><key>MediaID</key><uuid /><key>MediaURL</key><string /><key>MusicURL</key><string /><key>Name</key><string /><key>OtherCleanTime</key><integer>0</integer><key>OtherCount</key><integer>4096</integer><key>OtherPrims</key><integer>0</integer><key>OwnerID</key><uuid>dd1e79b2-ddfe-4080-8206-242ab63f4a19</uuid><key>OwnerPrims</key><integer>44</integer><key>ParcelFlags</key><binary encoding="base64">egAAAQ==</binary><key>ParcelPrimBonus</key><real>1</real><key>PassHours</key><real>1</real><key>PassPrice</key><integer>10</integer><key>PublicCount</key><integer>0</integer><key>RegionDenyAnonymous</key><boolean>0</boolean><key>RegionDenyIdentified</key><boolean>0</boolean><key>RegionDenyTransacted</key><boolean>0</boolean><key>RegionPushOverride</key><boolean>0</boolean><key>RentPrice</key><integer>0</integer><key>RequestResult</key><integer>0</integer><key>SalePrice</key><integer>10000</integer><key>SelectedPrims</key><integer>0</integer><key>SelfCount</key><integer>0</integer><key>SequenceID</key><integer>0</integer><key>SimWideMaxPrims</key><integer>3750</integer><key>SimWideTotalPrims</key><integer>44</integer><key>SnapSelection</key><boolean>0</boolean><key>SnapshotID</key><uuid /><key>Status</key><integer>0</integer><key>TotalPrims</key><integer>44</integer><key>UserLocation</key><array><real>0</real><real>0</real><real>0</real></array><key>UserLookAt</key><array><real>0</real><real>0</real><real>0</real></array></map></array></map><key>message</key><string>ParcelProperties</string></map><map><key>body</key><map><key>SimulatorInfo</key><array><map><key>Handle</key><binary encoding="base64">AAP8AAADxAA=</binary><key>IP</key><binary encoding="base64">2FISdw==</binary><key>Port</key><integer>13002</integer></map></array></map><key>message</key><string>EnableSimulator</string></map></array><key>id</key><integer>-182442759</integer></map></llsd>'

        data = llsd.parse(llsd_data)

        packets = self.eq._decode_eq_result(data)

        packet_names = []

        for packet in packets:
            self.assertEquals(str(type(packet)), '<class \'pyogp.lib.base.message.message.Message\'>')
            packet_names.append(packet.name)

        self.assertEquals(packet_names, ['AgentGroupDataUpdate', 'ParcelProperties', 'EnableSimulator'])
Esempio n. 10
0
    def __load(self, path):
        # circular imports, sorry, must import update locally
        import update

        if os.path.isabs(path):
            self.path = path
        else:
            abs_path = os.path.abspath(path)
            found_path = common.search_up_for_file(abs_path)
            if found_path is not None:
                self.path = found_path
            else:
                self.path = abs_path
        if os.path.isfile(self.path):
            autobuild_xml = file(self.path, 'rb').read()
            if not autobuild_xml:
                logger.warn("Configuration file '%s' is empty" % self.path)
                return
            try:
                saved_data = llsd.parse(autobuild_xml)
            except llsd.LLSDParseError:
                raise common.AutobuildError(
                    "Configuration file %s is corrupt. Aborting..." %
                    self.path)
            saved_data, orig_ver = update.convert_to_current(
                self.path, saved_data)
            # Presumably this check comes after format-version updates because
            # at some point in paleontological history the file format did not
            # include "type".
            if saved_data.get("type", None) != 'autobuild':
                raise common.AutobuildError(
                    self.path + ' not an autobuild configuration file')
            self.__init_from_dict(saved_data)
            logger.debug("Configuration file '%s'" % self.path)
            if orig_ver:
                logger.warn("Saving configuration file %s in format %s" %
                            (self.path, AUTOBUILD_CONFIG_VERSION))
                self.save()
                # We don't want orig_ver to appear in the saved file: that's
                # for internal use only. But we do want to track it because
                # there are those who care what kind of file we originally
                # read.
                self["orig_ver"] = orig_ver
        elif not os.path.exists(self.path):
            logger.warn("Configuration file '%s' not found" % self.path)
        else:
            raise ConfigurationError("cannot create configuration file %s" %
                                     self.path)
Esempio n. 11
0
    def __init__(self,
                 path=None,
                 stream=None,
                 parsed_llsd=None,
                 convert_platform=None,
                 create_quietly=False):
        self.version = AUTOBUILD_METADATA_VERSION
        self.type = AUTOBUILD_METADATA_TYPE
        self.build_id = None
        self.platform = None
        self.configuration = None
        self.package_description = None
        self.manifest = []
        self.dependencies = {}
        self.archive = None
        self.install_type = None
        self.install_dir = None
        self.dirty = False

        metadata_xml = None
        if path:
            self.path = path
            if os.path.isfile(self.path):
                metadata_xml = file(self.path, 'rb').read()
                if not metadata_xml:
                    logger.warn("Metadata file '%s' is empty" % self.path)
                    self.dirty = False
                    return
            elif not os.path.exists(self.path):
                if not create_quietly:
                    logger.warn("Configuration file '%s' not found" %
                                self.path)
        elif stream:
            metadata_xml = stream.read()
        if metadata_xml:
            try:
                parsed_llsd = llsd.parse(metadata_xml)
            except llsd.LLSDParseError:
                raise common.AutobuildError(
                    "Metadata file %s is corrupt. Aborting..." % self.path)

        if parsed_llsd:
            self.__load(parsed_llsd)
            self.update(parsed_llsd)
Esempio n. 12
0
    def __load(self, path=None):
        if os.path.isabs(path):
            self.path = path
        else:
            abs_path = os.path.abspath(path)
            found_path = common.search_up_for_file(abs_path)
            if found_path is not None:
                self.path = found_path
            else:
                self.path = abs_path
        if os.path.isfile(self.path):
            installed_xml = file(self.path, 'rb').read()
            if not installed_xml:
                logger.warn("Installed file '%s' is empty" % self.path)
                return
            logger.debug("Installed file '%s'" % self.path)
            try:
                saved_data = llsd.parse(installed_xml)
            except llsd.LLSDParseError:
                raise common.AutobuildError(
                    "Installed file %s is not valid. Aborting..." % self.path)
            if not (('version' in saved_data
                     and saved_data['version'] == self.version) and
                    ('type' in saved_data) and
                    (saved_data['type'] == AUTOBUILD_INSTALLED_TYPE)):
                raise common.AutobuildError(
                    self.path +
                    ' is not compatible with this version of autobuild.' +
                    '\nClearing your build directory and rebuilding should correct it.'
                )

            dependencies = saved_data.pop('dependencies', {})
            for (name, package) in dependencies.iteritems():
                self.dependencies[name] = package
            self.update(saved_data)
        elif not os.path.exists(self.path):
            logger.info("Installed packages file '%s' not found; creating." %
                        self.path)
        else:
            raise ConfigurationError(
                "cannot create installed packages file %s" % self.path)
Esempio n. 13
0
 def __load(self, path):
     if os.path.isabs(path):
         self.path = path
     else:
         abs_path = os.path.abspath(path)
         found_path = common.search_up_for_file(abs_path)
         if found_path is not None:
             self.path = found_path
         else:
             self.path = abs_path
     if os.path.isfile(self.path):
         try:
             saved_data = llsd.parse(file(self.path, 'rb').read())
         except llsd.LLSDParseError:
             raise AutobuildError("Config file %s is corrupt. Aborting..." % self.path)
         if not saved_data.has_key('version'):
             raise AutobuildError("incompatible configuration file %s\n"
                 "if this is a legacy format autobuild.xml file, please try the workaround found here:\n"
                 "https://wiki.lindenlab.com/wiki/Autobuild/Incompatible_Configuration_File_Error" % self.path)
         if saved_data['version'] == self.version:
             if (not saved_data.has_key('type')) or (saved_data['type'] != 'autobuild'):
                 raise AutobuildError(self.path + ' not an autobuild configuration file')
             package_description = saved_data.pop('package_description', None)
             if package_description is not None:
                 self.package_description = PackageDescription(package_description)
             installables = saved_data.pop('installables', {})
             for (name, package) in installables.iteritems():
                 self.installables[name] = PackageDescription(package)
             self.update(saved_data)
             logger.debug("Configuration file '%s'" % self.path)
         else:
             if saved_data['version'] in update.updaters:
                 update.updaters[saved_data['version']](saved_data, self)
             else:
                 raise ConfigurationError("cannot update version %s file %s" %
                                          (saved_data.version, self.path))
     elif not os.path.exists(self.path):
         logger.warn("Configuration file '%s' not found" % self.path)
     else:
         raise ConfigurationError("cannot create configuration file %s" % self.path)
Esempio n. 14
0
    def __call__(self, environ, start_response):
        self.environ = environ
        self.start = start_response
        self.request = Request(environ)
        self.response = Response()

        l = self.request.headers.get('Content-Length', '0')
        if l == '':
            l = '0'
        l = int(l)
        data = self.request.body

        # we assume it's LLSD for now and try to parse it
        # TODO: check headers
        try:
            data = llsd.parse(data)
            if data is False:  # might happen with GET
                data = {}
        except:
            self.response.status = 500
            return self.response(self.environ, self.start)

        if self.request.path == "/seed_cap":
            return self.handle_seedcap(data)
        elif self.request.path == "/seed_cap_wrong_content_type":
            return self.handle_seedcap(data, content_type="text/foobar")
        elif self.request.path == "/cap_wrong_content_type":
            return self.some_capability({}, content_type="text/foobar")
        elif self.request.path == "/cap/error":
            return self.send_response(500, 'error')
        elif self.request.path == "/cap/rez_avatar/place":
            return self.place_avatar(data)
        elif self.request.path == "/cap/some_capability":
            return self.some_capability(data)
        else:
            return self.send_response(404, 'resource not found.')
parser.add_argument(
    "outfilename",
    help="Name of CSV file to create",
)
args = parser.parse_args()

with open(args.infilename, "r") as slp_file:
    slps = slp_file.readlines()
    print "Reading from %s - %d items" % (args.infilename, len(slps))

    with open(args.outfilename, "w") as csv_file:

        print "Writing to %s" % args.outfilename

        for index, each_slp in enumerate(slps):
            slp_entry = llsd.parse(each_slp)

            first_key = slp_entry.keys()[0]

            # first entry so write column headers
            if index == 0:
                line = ""
                for key, value in slp_entry[first_key].iteritems():
                    line += key
                    line += ", "
                csv_file.write("entry, %s, \n" % line)
            # write line of data
            line = ""
            for key, value in slp_entry[first_key].iteritems():
                line += str(value)
                line += ", "
 def read_xml(self):
     # This approach reads the entire POST data into memory first
     return llsd.parse(self.read())