PageRenderTime 32ms CodeModel.GetById 15ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

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