PageRenderTime 35ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/im_funcs.rb

https://github.com/apirogov/Aphorism
Ruby | 305 lines | 184 code | 66 blank | 55 comment | 84 complexity | 2c3ef68ccdd8cb1314c20e6738566f22 MD5 | raw file
  1. #!/usr/bin/env ruby
  2. #Request Funcs for IM
  3. #Copyright (C) 2010 Anton Pirogov
  4. #Licensed under the GPLv3 or later
  5. #here all the fun happens :) this is an interface for the whole functionality...
  6. #the data contains all commands and data as JSON... responses are JSON objects too
  7. #evaluates the cmd string and does stuff and gives responses
  8. def evaluate_command(data)
  9. cmd = data['cmd']
  10. ownnick = params[:nickname]
  11. ## for description of the meaning see below (at function definitions)
  12. if cmd == 'add_contact'
  13. return add_contact(ownnick, data['nickname'])
  14. end
  15. if cmd == 'remove_contact'
  16. return remove_contact(ownnick, data['nickname'])
  17. end
  18. if cmd == 'request_auth'
  19. return request_auth(ownnick, data['to'], data['message'])
  20. end
  21. if cmd == 'grant_auth'
  22. return grant_auth(ownnick, data['nickname'])
  23. end
  24. if cmd == 'withdraw_auth'
  25. return withdraw_auth(ownnick, data['nickname'])
  26. end
  27. if cmd == 'get_pubkey'
  28. return get_publickey(data['nickname'])
  29. end
  30. if cmd == 'pull_clist'
  31. return pull_clist(ownnick)
  32. end
  33. if cmd == 'pull_queue'
  34. return pull_queue(ownnick)
  35. end
  36. if cmd == 'send_im'
  37. return send_im(ownnick, data['to'], data['message'])
  38. end
  39. if cmd == 'check_online_state'
  40. return check_online_state(ownnick,data['nickname'])
  41. end
  42. if cmd == 'delete_account' #can also happen :(
  43. return delete_account(ownnick, params[:password]) #huge cleanup function
  44. end
  45. #for demonstration - calculator...
  46. if cmd == 'calc'
  47. return calc(data['expression'])
  48. end
  49. #########################
  50. #nothing matched
  51. return {'response'=>false,'report'=>'Unknown command: '+cmd}
  52. end
  53. ################### Functions for all request types ###########################
  54. #helper function
  55. def ret_fail(text)
  56. {'response'=>false, 'report'=> text}
  57. end
  58. #helper function
  59. def ret_success
  60. {'response'=>true}
  61. end
  62. #demonstration and testing of the interface - a calculator
  63. def calc(expression)
  64. value = 0
  65. #check that its just numbers and + - * / and () -> no XSS xD
  66. return {'response'=>'error'} if expr.match(/[^0123456789\+\-\*\/\(\)]/) != nil
  67. #calculate
  68. value = eval(expression)
  69. #return JSON response
  70. return {'response'=>value}
  71. end
  72. #add a contact to clist.. without authorization
  73. def add_contact(ownnick, person)
  74. return ret_fail('can not add yourself') if person == ownnick #you cant add yourself !
  75. usr = User.first(:nickname=>person)
  76. return ret_fail('no such user') if usr == nil #user does not exist !
  77. ownusr = User.first(:nickname => ownnick) #get own record to get the contact list
  78. return ret_fail('already in list') if ownusr.contacts.first(:userid => usr.id) != nil
  79. c = ownusr.contacts.new
  80. c.userid = usr.id
  81. puts usr.id
  82. puts c.userid
  83. c.authgiven = 'f'
  84. c.save
  85. return ret_success
  86. end
  87. #client removes contact
  88. def remove_contact(ownnick, person)
  89. usr = User.first(:nickname=>ownnick)
  90. return ret_fail('not found') if usr == nil #user not found (can only occur after own account deletion...as a bug)
  91. otherusr = User.first(:nickname=>person)
  92. c = usr.contacts.first(:userid => otherusr.id)
  93. return ret_fail('not in clist') if c == nil #user not in contact list
  94. #check whether the other user is given authorization
  95. if otherusr != nil #user exists? if not, doesnt matter...
  96. hiscontact = otherusr.contacts.first(:userid => usr.id)
  97. if hiscontact != nil #requesting user is in the clist of the user to be removed?
  98. return ret_fail('withdraw authorization first!') if hiscontact.authgiven == 't'
  99. hiscontact.authgiven = 'f'
  100. hiscontact.save
  101. end
  102. end
  103. #remove user from list
  104. c.destroy
  105. return ret_success
  106. end
  107. #client requests own contact list -> return list with nicks, permissions and online states
  108. def pull_clist(nick)
  109. usr = User.first(:nickname=>nick)
  110. return ret_fail('not found') if usr == nil #user not found (can only occur after own account deletion...as a bug)
  111. clist = Hash.new
  112. clist['nicks'] = []
  113. usr.contacts.each do |c|
  114. clist['nicks'].push User.first(:id => c.userid).nickname
  115. end
  116. #check the online states of the users and add to the list to be returned
  117. clist['state'] = Array.new
  118. clist['nicks'].each{ |contactnick|
  119. clist['state'].push check_online_state(nick, contactnick)['state']
  120. }
  121. return {'response'=>true, 'clist'=>clist} #return data
  122. end
  123. #client sends an instant message (authorization request without authorization, other require authorization)
  124. #this method handles auth_requests too... (called by auth_request(...) )
  125. def send_im(fromnick,tonick,message)
  126. return ret_fail('can not send IM to yourself') if fromnick==tonick #self-explainatory
  127. usr = User.first(:nickname=>fromnick) #find own record
  128. return ret_fail('own user not found') if usr == nil #must be error
  129. tousr = User.first(:nickname=>tonick)
  130. return ret_fail('addressee not found') if tousr == nil #must be error
  131. contact = usr.contacts.first(:userid => tousr.id)
  132. return ret_fail('not in list') if contact == nil #that nick is not in contact list!
  133. if message['type'] != 'auth_request' #Normal message
  134. return ret_fail('not authorized') if contact.authgiven != 't' #failure: not authorized!
  135. else #its an authorization request!
  136. return ret_fail('already given') if contact.authgiven == 't' #already has permission!
  137. return ret_fail('already sent') if contact.authgiven == 'p' #already sent a request!
  138. #ok... its a valid auth request... so set state to 'p' and update db record
  139. contact.authgiven = 'p' #awaiting to be answered
  140. contact.save
  141. end
  142. #append message to addressees message queue, update database record
  143. msg = tousr.messages.new
  144. msg.data = JSON.generate({'from'=>fromnick, 'message'=>message})
  145. msg.save
  146. return ret_success
  147. end
  148. #client asks for all queued messages from other users (IMs and auth requests and stuff)
  149. #returns empty list if empty... so always succeeds
  150. def pull_queue(ownnick)
  151. usr = User.first(:nickname=>ownnick) #find own record
  152. return ret_fail(ownnick+' not found in db') if usr == nil #must be error
  153. messages = []
  154. usr.messages.each do |msg|
  155. messages << msg.data
  156. msg.destroy
  157. end
  158. return {'response'=>true, 'messages'=> messages} #return message queue
  159. end
  160. #client asks another user for authorization
  161. def request_auth(from, to, message)
  162. #handeled by send_im
  163. return send_im(from, to, message)
  164. end
  165. #client gives permission to contact
  166. def grant_auth(from, to)
  167. ownusr = User.first(:nickname=>from)
  168. tousr = User.first(:nickname=>to)
  169. return ret_fail('user not found!') if tousr == nil #the user is not in the database
  170. contact = ownusr.contacts.first(:userid => tousr.id)
  171. return ret_fail(to+' not in your contact list') if contact==nil #first add the user before accepting auth_request!
  172. #check the authorization state
  173. hiscontact = tousr.contacts.first(:userid => ownusr.id)
  174. return ret_fail('not in list of '+to) if hiscontact==nil
  175. return ret_fail(to+' is already authorized') if hiscontact.authgiven =='t'
  176. return ret_fail('authorization was not requested') if hiscontact.authgiven=='f' #makes no sense to grant before getting a request...
  177. #so state is pending -> lets change to given
  178. hiscontact.authgiven = 't'
  179. hiscontact.save
  180. #append notification to queue and save db record
  181. tousr.messages.create(:data => JSON.generate({'type'=>'auth_grant','from'=>from}))
  182. return ret_success
  183. end
  184. #client withdraws permission to contact / denies a request...
  185. def withdraw_auth(from, to)
  186. tousr = User.first(:nickname=>to)
  187. return ret_fail('user not found!') if tousr == nil #the user is not in the database
  188. #check the authorization state
  189. contact = tousr.contacts.first(:userid => User.first(:nickname=>from).id)
  190. return ret_fail('not in list of '+to) if contact == nil
  191. return ret_fail(to+' is not authorized') if contact.authgiven=='f' #nothing to withdraw
  192. #set state to f (no permission to write to "from")
  193. oldstate = contact.authgiven #save old state
  194. contact.authgiven = 'f'
  195. contact.save
  196. #append notification...
  197. oldstate == 'p' ? type = 'auth_deny' : type = 'auth_withdraw' #if pending -> deny, if given -> withdraw
  198. tousr.messages.create(:data => JSON.generate({'type'=>type, 'from'=>from}))
  199. return ret_success
  200. end
  201. #check online state of a contact
  202. def check_online_state(source,nick,srcclist=nil)
  203. usr = User.first(:nickname=>source)
  204. return ret_fail(source+' not found') if usr == nil #user not found (can only occur after own account deletion...as a bug)
  205. #if user not in contact list -> request denied
  206. contact = usr.contacts.first(:userid => User.first(:nickname => nick).id)
  207. return ret_fail(nick+' not in your contact list') if contact == nil
  208. #check now authorization - if no authorization, dont tell state (no permission) but thats not a ret_fail!
  209. return {'response'=>true,'state'=>'hidden'} if contact.authgiven != 't'
  210. #everything's fine -> get state
  211. if $sessions.index(nick) != nil #that user has a session running -> online
  212. state = 'online'
  213. else
  214. state = 'offline'
  215. end
  216. return {'response'=>true, 'state'=>state}
  217. end
  218. #helper function - used by login/logout
  219. #send notification to contacts on login/logout
  220. #nick - who logged in/out?, state - 'online'/'offline'
  221. def broadcast_state(nick, state)
  222. #TODO: write function
  223. #get users clist, get the contacts clists and check their authorization to get the state
  224. #if its fine -> send the message to their queues
  225. return true
  226. end
  227. def delete_account(nick, password)
  228. #check whether logged in and correct password hash supplied
  229. usr = User.first(:nickname=>nick)
  230. return ret_fail('user does not exist!') if usr==nil #wtf?
  231. return ret_fail('password incorrect!') if password==usr.pwdhash #not matching
  232. #TODO: remove user from all his contacts and stuffz
  233. #ok... remove account :(
  234. usr.destroy #remove user from database
  235. #remove session
  236. $sessions.delete(nick) #remove session associacion
  237. return ret_success
  238. end
  239. #TODO: got_message message to notify the sender that a message was successfully delivered (identified by.. hash?)