/gal3rest/gal3rest.go

https://code.google.com/p/gal3upload/ · Go · 359 lines · 246 code · 40 blank · 73 comment · 33 complexity · bd0cdb9e1b9e9ae675f322625e3c9761 MD5 · raw file

  1. //Copyright (c) 2012 Tim Shannon
  2. //
  3. //Permission is hereby granted, free of charge, to any person obtaining a copy
  4. //of this software and associated documentation files (the "Software"), to deal
  5. //in the Software without restriction, including without limitation the rights
  6. //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. //copies of the Software, and to permit persons to whom the Software is
  8. //furnished to do so, subject to the following conditions:
  9. //
  10. //The above copyright notice and this permission notice shall be included in
  11. //all copies or substantial portions of the Software.
  12. //
  13. //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. //THE SOFTWARE.
  20. //gal3rest packages handles all access to the gallery3 rest api
  21. // and is meant to abstract accessing it
  22. package gal3rest
  23. import (
  24. "bytes"
  25. "encoding/json"
  26. "fmt"
  27. "io/ioutil"
  28. "log"
  29. "mime"
  30. "net/http"
  31. "net/url"
  32. "path"
  33. "reflect"
  34. "strconv"
  35. "strings"
  36. "crypto/tls"
  37. )
  38. //Client holds the url and API keys for the gallery being used
  39. // so you don't have to specify the url and api key for each request
  40. // all gallery access functions are attached to this type
  41. type Client struct {
  42. Url string
  43. APIKey string
  44. }
  45. //RestData contains the general format of data returned from REST
  46. // API calls to Gallery3
  47. type RestData struct {
  48. Url string
  49. Entity Entity
  50. Members []string
  51. Relationships map[string]interface{} // may Type this later
  52. }
  53. //RestCreate holds the structure for REST API requests to create a new
  54. // gallery
  55. type RestCreate struct {
  56. Type string `json:"type"`
  57. Name string `json:"name"`
  58. Title string `json:"title"`
  59. }
  60. //Entity is a general json data structure that is attached to most items
  61. // in the gallery3 rest api
  62. type Entity struct {
  63. Id int
  64. Description string
  65. Name string
  66. Web_url string
  67. Mime_type string
  68. Title string
  69. Type string
  70. Album_cover string
  71. Thumb_url string
  72. Thumb_url_public string
  73. Width int
  74. Thumb_width int
  75. Resize_width int
  76. Resize_height int
  77. View_count int
  78. Sort_order int
  79. Height int
  80. Updated int
  81. Captured int
  82. View_1 string
  83. Can_edit int
  84. View_2 string
  85. Thumb_size int
  86. Level int
  87. Created int
  88. Sort_column string
  89. Slug string
  90. Rand_key int
  91. Thumb_height int
  92. Owner_id int
  93. }
  94. //String function for pretty printing the entity REST data
  95. func (entity *Entity) String() string {
  96. var strValue string
  97. ref := reflect.ValueOf(entity).Elem()
  98. entityType := ref.Type()
  99. for i := 0; i < ref.NumField(); i++ {
  100. field := ref.Field(i)
  101. strValue += fmt.Sprintf("%s: %s %v\n",
  102. entityType.Field(i).Name,
  103. field.Type(), field.Interface())
  104. }
  105. return strValue
  106. }
  107. //Album is the somewhat abstracted type used to hold the relationship
  108. // between albums and photos, as well as the REST API data associated to them
  109. type Album struct {
  110. Entity
  111. Photos []*Photo
  112. Albums []*Album
  113. }
  114. //entity types
  115. const (
  116. PHOTO = "photo"
  117. ALBUM = "album"
  118. )
  119. //Photo is the abstraction of the REST entity
  120. type Photo struct {
  121. Entity
  122. }
  123. //Response is the json response from creating an album or uploading a photo
  124. // it simply contains the REST URL to the item added
  125. type Response struct {
  126. Url string
  127. }
  128. //Returns a new client for accessing the Galllery3 REST API
  129. func NewClient(url string, apiKey string) Client {
  130. client := Client{Url: url, APIKey: apiKey}
  131. client.checkClient()
  132. return client
  133. }
  134. //GetRESTItem retrieves an arbitrary REST item from a Gallery3
  135. // Possible key value parameters:
  136. // scope: direct - only specific item
  137. // all - All descendants (recursive) doesn't seem to work
  138. // name: value - only return items containing value
  139. // random: true - returns a single random item
  140. // type: photo
  141. // movie
  142. // album
  143. func (gClient *Client) GetRESTItem(itemUrl string,
  144. parameters map[string]string) (restData *RestData, status int, err error) {
  145. restData = new(RestData)
  146. tr := &http.Transport{
  147. TLSClientConfig: &tls.Config{InsecureSkipVerify : true},
  148. }
  149. hClient := &http.Client{Transport: tr}
  150. // hClient := new(http.Client)
  151. if parameters != nil {
  152. urlValues := url.Values{}
  153. for k, v := range parameters {
  154. urlValues.Set(k, v)
  155. }
  156. itemUrl += "?" + urlValues.Encode()
  157. }
  158. req, _ := http.NewRequest("GET", itemUrl, nil)
  159. req.Header.Set("X-Gallery-Request-Method", "GET")
  160. req.Header.Set("X-Gallery-Request-Key", gClient.APIKey)
  161. response, err := hClient.Do(req)
  162. if err != nil {
  163. return
  164. // log.Panic("Error connecting to: "+itemUrl+" Error: ", err)
  165. }
  166. body, err := ioutil.ReadAll(response.Body)
  167. response.Body.Close()
  168. if err != nil {
  169. return
  170. //log.Panic("Error reading response: ", err)
  171. }
  172. json.Unmarshal(body, &restData)
  173. status = response.StatusCode
  174. return
  175. }
  176. //checkClient checks to make sure the URL and API is set and configured properly
  177. func (gClient *Client) checkClient() {
  178. if gClient.Url == "" {
  179. log.Panicln("No URL specified in the client." +
  180. " Be sure to specify the REST url before making a request")
  181. } else {
  182. if gClient.Url[len(gClient.Url)-1:] != "/" {
  183. gClient.Url += "/"
  184. }
  185. }
  186. if gClient.APIKey == "" {
  187. log.Panicln("No API key specified in the client. " +
  188. "Be sure to specify the REST API key before making a request.")
  189. }
  190. }
  191. //GetUrlFromId simply builds the REST url from the passed in ID
  192. func (gClient *Client) GetUrlFromId(id int) string {
  193. gClient.checkClient()
  194. return gClient.Url + "rest/item/" + strconv.Itoa(id)
  195. }
  196. func (gClient *Client) GetItemsUrl() string {
  197. gClient.checkClient()
  198. return gClient.Url + "rest/items?"
  199. }
  200. //CreateAlbum creates an album with the passed in name and title inside the album at the
  201. // passed in url
  202. func (gClient *Client) CreateAlbum(title string, name string, parentUrl string) (itemUrl string, status int, err error) {
  203. gClient.checkClient()
  204. tr := &http.Transport{
  205. TLSClientConfig: &tls.Config{InsecureSkipVerify : true},
  206. }
  207. hClient := &http.Client{Transport: tr}
  208. // hClient := new(http.Client)
  209. c := &RestCreate{Name: name, Title: title, Type: ALBUM}
  210. b, err := json.Marshal(c)
  211. if err != nil {
  212. return
  213. // log.Panicln("Error marshalling Rest create: ", jErr)
  214. }
  215. //base64.URLEncoding.EncodeToString
  216. encodedValue := "entity=" + url.QueryEscape(string(b))
  217. buffer := bytes.NewBuffer([]byte(encodedValue))
  218. req, _ := http.NewRequest("POST", parentUrl, buffer)
  219. req.Header.Set("X-Gallery-Request-Method", "POST")
  220. req.Header.Set("X-Gallery-Request-Key", gClient.APIKey)
  221. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  222. req.Header.Set("Content-Length", strconv.Itoa(len(encodedValue)))
  223. response, err := hClient.Do(req)
  224. if err != nil {
  225. return
  226. //log.Panic("Error connecting to: "+parentUrl+" Error: ", err)
  227. }
  228. rspValue, err := ioutil.ReadAll(response.Body)
  229. if err != nil {
  230. return
  231. //log.Panic("Error reading response: ", err)
  232. }
  233. response.Body.Close()
  234. itemUrl = getUrl(rspValue)
  235. status = response.StatusCode
  236. return
  237. }
  238. //Uploads an image to the passed in album ur
  239. // sections of this should probabaly be scrapped and replaced
  240. // with the go mime/multipart package
  241. // a lot of this is hardcoded which could cause issues in the future
  242. func (gClient *Client) UploadImage(title string, imagePath string,
  243. parentUrl string) (url string, status int, err error) {
  244. gClient.checkClient()
  245. tr := &http.Transport{
  246. TLSClientConfig: &tls.Config{InsecureSkipVerify : true},
  247. }
  248. hClient := &http.Client{Transport: tr}
  249. // hClient := new(http.Client)
  250. _, name := path.Split(imagePath)
  251. c := &RestCreate{Name: name, Title: title, Type: PHOTO}
  252. entity, err := json.Marshal(c)
  253. if err != nil {
  254. return
  255. //log.Panicln("Error marshalling Rest create: ", jErr)
  256. }
  257. file, err := ioutil.ReadFile(imagePath)
  258. if err != nil {
  259. return url, status, err
  260. // log.Panic("Error reading the image file: ", fErr)
  261. }
  262. var dataParts = make([]string, 13)
  263. boundry := "roPK9J3DoG4ZWP6etiDuJ97h-zeNAph"
  264. //build multipart request
  265. dataParts[0] = "--" + boundry
  266. dataParts[1] = `Content-Disposition: form-data; name="entity"`
  267. dataParts[2] = "Content-Type: text/plain; charset=UTF-8"
  268. dataParts[3] = "Content-Transfer-Encoding: 8bit"
  269. //space in 4
  270. dataParts[5] = string(entity)
  271. dataParts[6] = "--" + boundry
  272. dataParts[7] = `Content-Disposition: form-data; name="file";` +
  273. `filename="` + path.Base(imagePath) + `"`
  274. dataParts[8] = "Content-Type: " + getContentType(imagePath)
  275. dataParts[9] = "Content-Transfer-Encoding: binary"
  276. //space in 10
  277. dataParts[11] = string(file)
  278. dataParts[12] = "--" + boundry
  279. data := strings.Join(dataParts, "\n")
  280. buffer := bytes.NewBuffer([]byte(data))
  281. req, err := http.NewRequest("POST", parentUrl, buffer)
  282. if err != nil {
  283. return
  284. }
  285. req.Header.Set("X-Gallery-Request-Method", "POST")
  286. req.Header.Set("X-Gallery-Request-Key", gClient.APIKey)
  287. req.Header.Set("Content-Type", "multipart/form-data; boundary="+boundry)
  288. req.Header.Set("Content-Length", strconv.Itoa(len(data)))
  289. //fmt.Println("request: ", req)
  290. response, err := hClient.Do(req)
  291. if err != nil {
  292. return
  293. // log.Panic("Error connecting to: "+parentUrl+" Error: ", err)
  294. }
  295. rspValue, err := ioutil.ReadAll(response.Body)
  296. if err != nil {
  297. return
  298. }
  299. response.Body.Close()
  300. url = getUrl(rspValue)
  301. status = response.StatusCode
  302. err = nil
  303. return
  304. }
  305. //Returns the proper mime type for the passed in file
  306. func getContentType(file string) string {
  307. ext := path.Ext(file)
  308. mType := mime.TypeByExtension(ext)
  309. if mType == "" {
  310. return "application/octet-stream"
  311. }
  312. return mType
  313. }
  314. //Pull URL out of rest response
  315. func getUrl(data []byte) string {
  316. response := new(Response)
  317. json.Unmarshal(data, &response)
  318. return response.Url
  319. }