diff --git a/httpupload.go b/httpupload.go index 67d418b..624dd34 100644 --- a/httpupload.go +++ b/httpupload.go @@ -7,6 +7,7 @@ package main import ( "bytes" "encoding/xml" + "fmt" "log" "net/http" "os" @@ -14,14 +15,12 @@ import ( "regexp" "strconv" - "github.com/gabriel-vasile/mimetype" // MIT License + "github.com/beevik/etree" // BSD-2-clause + "github.com/gabriel-vasile/mimetype" // MIT License) "github.com/mattn/go-xmpp" // BSD-3-Clause ) func httpUpload(client *xmpp.Client, jserver string, filePath string) string { - var iqDiscoItemsXML IQDiscoItemsType - var iqDiscoInfoXML IQDiscoInfoType - var iqHttpUploadSlotXML IQHttpUploadSlot var uploadComponent string var maxFileSize int64 @@ -59,14 +58,18 @@ func httpUpload(client *xmpp.Client, jserver string, filePath string) string { if err != nil { log.Fatal(err) } - err = xml.Unmarshal(iqContent.Query, &iqDiscoItemsXML) + iqDiscoItemsXML := etree.NewDocument() + err = iqDiscoItemsXML.ReadFromBytes(iqContent.Query) if err != nil { log.Fatal(err) } + iqDiscoItemsXMLQuery := iqDiscoItemsXML.SelectElement("query") + iqDiscoItemsXMLItems := iqDiscoItemsXMLQuery.SelectElements("item") // Check the services reported by disco#items for the http upload service - for _, r := range iqDiscoItemsXML.Item { - iqDiscoInfo, err := sendIQ(client, r.Jid, "get", + for _, r := range iqDiscoItemsXMLItems { + jid := r.SelectAttr("jid") + iqDiscoInfo, err := sendIQ(client, jid.Value, "get", "") if err != nil { log.Fatal(err) @@ -74,23 +77,34 @@ func httpUpload(client *xmpp.Client, jserver string, filePath string) string { if iqDiscoInfo.Type != "result" { continue } - err = xml.Unmarshal(iqDiscoInfo.Query, &iqDiscoInfoXML) + iqDiscoInfoXML := etree.NewDocument() + err = iqDiscoInfoXML.ReadFromBytes(iqDiscoInfo.Query) if err != nil { log.Fatal(err) } - - if iqDiscoInfoXML.Identity.Type == "file" && iqDiscoInfoXML.Identity.Category == "store" { - uploadComponent = r.Jid + iqDiscoInfoXMLQuery := iqDiscoInfoXML.SelectElement("query") + iqDiscoInfoXMLIdentity := iqDiscoInfoXMLQuery.SelectElement("identity") + iqDiscoInfoXMLType := iqDiscoInfoXMLIdentity.SelectAttr("type") + iqDiscoInfoXMLCategory := iqDiscoInfoXMLIdentity.SelectAttr("category") + + if iqDiscoInfoXMLType.Value == "file" && + iqDiscoInfoXMLCategory.Value == "store" { + uploadComponent = jid.Value } } if uploadComponent == "" { log.Fatal("No http upload component found.") } - for _, r := range iqDiscoInfoXML.X { - for i, t := range r.Field { - if t.Var == "max-file-size" && r.Field[i-1].Value == nsHttpUpload { - maxFileSize, err = strconv.ParseInt(t.Value, 10, 64) + iqDiscoInfoXMLX := iqDiscoItemsXMLQuery.SelectElements("x") + for _, r := range iqDiscoInfoXMLX { + field := r.SelectElements("field") + for i, t := range field { + varAttr := t.SelectAttr("var") + prevFieldVal := field[i-1].SelectElement("value") + curFieldVal := t.SelectElement("value") + if varAttr.Value == "max-file-size" && prevFieldVal.Text() == nsHttpUpload { + maxFileSize, err = strconv.ParseInt(curFieldVal.Text(), 10, 64) if err != nil { log.Fatal("Error while checking server maximum http upload file size.") } @@ -108,40 +122,48 @@ func httpUpload(client *xmpp.Client, jserver string, filePath string) string { } } - var request IQHttpUploadSlotRequest - request.Xmlns = nsHttpUpload - request.FileName = fileNameEscaped - request.FileSize = fileSize - request.FileType = mimeType - r, err := xml.Marshal(request) + request := etree.NewDocument() + requestReq := request.CreateElement("request") + requestReq.CreateAttr("xmlns", nsHttpUpload) + requestReq.CreateAttr("filename", fileNameEscaped) + requestReq.CreateAttr("size", fmt.Sprint(fileSize)) + requestReq.CreateAttr("content-type", mimeType) + r, err := request.WriteToString() if err != nil { log.Fatal(err) } // Request http upload slot - uploadSlot, err := sendIQ(client, uploadComponent, "get", string(r)) + uploadSlot, err := sendIQ(client, uploadComponent, "get", r) if err != nil { log.Fatal(err) } if uploadSlot.Type != "result" { log.Fatal("Error while requesting upload slot.") } - err = xml.Unmarshal(uploadSlot.Query, &iqHttpUploadSlotXML) + iqHttpUploadSlotXML := etree.NewDocument() + err = iqHttpUploadSlotXML.ReadFromBytes(uploadSlot.Query) if err != nil { log.Fatal(err) } + iqHttpUploadSlotXMLSlot := iqHttpUploadSlotXML.SelectElement("slot") + iqHttpUploadSlotXMLPut := iqHttpUploadSlotXMLSlot.SelectElement("put") + iqHttpUploadSlotXMLPutURL := iqHttpUploadSlotXMLPut.SelectAttr("url") // Upload file httpClient := &http.Client{} - req, err := http.NewRequest(http.MethodPut, iqHttpUploadSlotXML.Put.URL, buffer) + req, err := http.NewRequest(http.MethodPut, iqHttpUploadSlotXMLPutURL.Value, + buffer) if err != nil { log.Fatal(err) } req.Header.Set("Content-Type", mimeTypeEscaped.String()) - for _, h := range iqHttpUploadSlotXML.Put.Headers { - switch h.Name { + iqHttpUploadSlotXMLPutHeaders := iqHttpUploadSlotXMLPut.SelectElements("header") + for _, h := range iqHttpUploadSlotXMLPutHeaders { + name := h.SelectAttr("name") + switch name.Value { case "Authorization", "Cookie", "Expires": - req.Header.Set(h.Name, h.Value) + req.Header.Set(name.Value, h.Text()) } } resp, err := httpClient.Do(req) @@ -154,5 +176,7 @@ func httpUpload(client *xmpp.Client, jserver string, filePath string) string { } // Return http link - return iqHttpUploadSlotXML.Get.URL + iqHttpUploadSlotXMLGet := iqHttpUploadSlotXMLSlot.SelectElement("get") + iqHttpUploadSlotXMLGetURL := iqHttpUploadSlotXMLGet.SelectAttr("url") + return iqHttpUploadSlotXMLGetURL.Value } diff --git a/iqstructs.go b/iqstructs.go deleted file mode 100644 index 4e4bbf7..0000000 --- a/iqstructs.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2022 Martin Dosch. -// Use of this source code is governed by the BSD-2-clause -// license that can be found in the LICENSE file. - -package main - -import "encoding/xml" - -// Created with https://github.com/miku/zek -type IQDiscoItemsType struct { - XMLName xml.Name `xml:"query"` - Text string `xml:",chardata"` - Xmlns string `xml:"xmlns,attr"` - Item []struct { - Text string `xml:",chardata"` - Jid string `xml:"jid,attr"` - } `xml:"item"` -} - -// Created with https://github.com/miku/zek -type IQDiscoInfoType struct { - XMLName xml.Name `xml:"query"` - Text string `xml:",chardata"` - Xmlns string `xml:"xmlns,attr"` - Identity struct { - Text string `xml:",chardata"` - Type string `xml:"type,attr"` - Name string `xml:"name,attr"` - Category string `xml:"category,attr"` - } `xml:"identity"` - Feature []struct { - Text string `xml:",chardata"` - Var string `xml:"var,attr"` - } `xml:"feature"` - X []struct { - Text string `xml:",chardata"` - Type string `xml:"type,attr"` - Xmlns string `xml:"xmlns,attr"` - Field []struct { - Text string `xml:",chardata"` - Type string `xml:"type,attr"` - Var string `xml:"var,attr"` - Value string `xml:"value"` - } `xml:"field"` - } `xml:"x"` -} - -type IQHttpUploadSlotRequest struct { - XMLName xml.Name `xml:"request"` - Xmlns string `xml:"xmlns,attr"` - FileName string `xml:"filename,attr"` - FileType string `xml:"mime-type,attr"` - FileSize int64 `xml:"size,attr"` -} - -// Created with https://github.com/miku/zek -type IQHttpUploadSlot struct { - XMLName xml.Name `xml:"slot"` - Text string `xml:",chardata"` - Xmlns string `xml:"xmlns,attr"` - Get struct { - Text string `xml:",chardata"` - URL string `xml:"url,attr"` - } `xml:"get"` - Put struct { - Text string `xml:",chardata"` - URL string `xml:"url,attr"` - Headers []struct { - Name string `xml:"name,attr"` - Value string `xml:",chardata"` - } `xml:"header"` - } `xml:"put"` -}