From 114aadc4c45e9d1b542728831c50161b3d3cdb6e Mon Sep 17 00:00:00 2001 From: Abhishek Sinha Date: Sat, 1 Dec 2018 10:02:04 +0530 Subject: [PATCH] fixed mongoose string length issue, deposit buy sell done --- mongoose.h | 2 +- supernode/index.html | 447 ++++++++++++++++++++++++++++++------- supernode/websocket_chat | Bin 247376 -> 247376 bytes supernode/websocket_chat.c | 2 +- 4 files changed, 364 insertions(+), 87 deletions(-) diff --git a/mongoose.h b/mongoose.h index 2667c75..3933ff1 100644 --- a/mongoose.h +++ b/mongoose.h @@ -6638,4 +6638,4 @@ struct mg_iface *mg_socks_mk_iface(struct mg_mgr *, const char *proxy_addr); #endif /* __cplusplus */ #endif -#endif +#endif \ No newline at end of file diff --git a/supernode/index.html b/supernode/index.html index d047cc5..a493006 100644 --- a/supernode/index.html +++ b/supernode/index.html @@ -7892,7 +7892,7 @@ /*************************************************** GET EQUIVALENT BTC HERE IN TERMS OF ORDERED INR I.E 10K, 50K... ******************************************************/ - params.bitcoinToBePaid = 100000000; // in Satoshis + params.bitcoinToBePaid = localbitcoinplusplus.trade.prototype.calculateBTCEquivalentOfCash.call(params.depositing_amount); let receivedTradeInfo = {...params}; @@ -7903,19 +7903,32 @@ } // Send the address to the requester - let requester_data = - `Please send the Bitcoin to ${generate_btc_keys_for_requester.address}.`; + let requester_data ={ + msg: `Please send the Bitcoin to ${generate_btc_keys_for_requester.address}.`, + deposit_db_object: receivedTradeInfo + }; return { error: false, method: "deposit_asset_request_response", data: requester_data }; } else if (params.product == "INR") { + params.status = 1; + let receivedTradeInfo = {...params}; + + try { + addDB("deposit", receivedTradeInfo); + } catch (error) { + throw new Error(error); + } + // YOU NEED TO DETERMINE A BANK ACCOUNT HERE let bank_account = 'Name: John Doe, State Bank of India, Branch: Ashok Nagar Ranchi'; - let requester_data = - `Plese send the money to following bank address: "${bank_account}"`; + let requester_data ={ + msg: `Plese send the money to following bank address: "${bank_account}"`, + deposit_db_object: receivedTradeInfo + }; return { error: false, method: "deposit_asset_request_response", @@ -8000,17 +8013,23 @@ } else { this.errors.push("No receiving BTC or Bank address provided."); } - if (receiving_address.trim() !== "") { - this.receiving_address = receiving_address; - } else { - this.errors.push("No receiving BTC or Bank address provided."); - } + // if (receiving_address.trim() !== "") { + // this.receiving_address = receiving_address; + // } else { + // this.errors.push("No receiving BTC or Bank address provided."); + // } if (typeof localbitcoinplusplus.master_configurations.validAssets !== 'undefined' && localbitcoinplusplus.master_configurations.validAssets.indexOf(product) >= 0) { this.product = product; } else { this.errors.push("Invalid product."); } + if (typeof localbitcoinplusplus.master_configurations.validAssets !== 'undefined' && + localbitcoinplusplus.master_configurations.validAssets.indexOf(currency) >= 0) { + this.currency = currency; + } else { + this.errors.push("Invalid product."); + } if (typeof buy_price == "number" && buy_price > 0) { this.buy_price = buy_price; } else { @@ -8049,7 +8068,7 @@ let placeNewOrder = localbitcoinplusplus.rpc.prototype.send_rpc.call(this, this.rpc_job, { "order_type": this.order_type, - "user_flo_address": this.user_flo_address, + "trader_flo_address": this.user_flo_address, "product": this.product, "currency": this.currency, "buy_price": this.buy_price, @@ -8154,6 +8173,49 @@ if (params.order_type != "buy" || params.product != "BTC" || params.currency != "INR") { throw new Error("Invalid buy request."); } + // "order_type": this.order_type, + // "user_flo_address": this.user_flo_address, + // "product": this.product, + // "currency": this.currency, + // "buy_price": this.buy_price, + // "buyer_public_key": this.buyer_public_key, + // "buyer_key_signature": this.buyer_key_signature, + // "order_validator_public_key": this.order_validator_public_key, + // "receiving_address": this.receiving_address + + //Check buyer's INR balance + readDBbyIndex("cash_balances", "trader_flo_address", params.trader_flo_address, function(res) { + if(!isNaN(res.cash_balance)) { + let buyer_cash_balance = parseFloat(res.cash_balance); + let buy_price_btc = parseFloat(params.buy_price); + if (buyer_cash_balance < buy_price_btc) { + throw new Error("Insufficient balance."); + } + // calculate equivalent BTC for x amount of Cash + let eqBTC = this.calculateBTCEquivalentOfCash("INR", buy_price_btc); + + if (!isNaN(eqBTC)) { + let eqBTC = parseFloat(eqBTC); + + // Descrease INR balance of user in cash table + res.cash_balance = buyer_cash_balance - buy_price_btc; + updateinDB("cash_balances", res, params.trader_flo_address); + + // Increase BTC balance of buyer with extra eqBTC amount of BTC + readDBbyIndex("btc_balances", "trader_flo_address", params.trader_flo_address, function(res) { + if (typeof res.btc_balance == "undefined" && !NaN(res.btc_balance)) { + res.btc_balance = parseFloat(res.btc_balance)+eqBTC; + updateinDB("btc_balances", res, params.trader_flo_address); + } else { + console.error("Failed to read the Bitcoin balance from DB."); + } + }); + } else { + throw new Error("Failed to fetch cuurent BTC price."); + } + } + }); + params['rand_id'] = Math.floor(Math.random(1, 1000) * 1000); addDB("buyOrders", params); callback(); @@ -8161,15 +8223,54 @@ trade_sell(params, callback) { for (var key in params) { if (params.hasOwnProperty(key)) { - if (typeof key == undefined || key.trim() == "" || key == null) { + if (typeof key == "undefined" || key.trim() == "" || key == null) { throw new Error("Incomplete or invalid request!"); } } } - if (params.order_type != "sell" || params.product != "BTC" || params.currency != "INR") { + console.log(params.buy_price); + + console.log(localbitcoinplusplus.master_configurations.validTradingAmount.includes(params.buy_price)); + + if (params.order_type != "sell" || params.product != "BTC" || params.currency != "INR" + && localbitcoinplusplus.master_configurations.validTradingAmount.includes(params.buy_price)) { throw new Error("Invalid sell request."); } + // Check BTC balance of the seller + console.log(params.trader_flo_address); + + readDBbyIndex("btc_balances", "trader_flo_address", params.trader_flo_address, function(res) { + if (typeof res.trader_flo_address == "string" && res.trader_flo_address.length>0 + && typeof res.btc_balance == "number" && res.btc_balance>0) { + let seller_btc_balance = parseFloat(res.btc_balance); + let sell_price_in_inr = parseFloat(params.buy_price); + let eqBTC = this.calculateBTCEquivalentOfCash("INR", sell_price_in_inr); + console.log(eqBTC); + + if (!isNaN(eqBTC)) { + let eqBTC = parseFloat(eqBTC); + if (seller_btc_balance < eqBTC) { + throw new Error("Insufficient BTC balance."); + } + // Decrease BTC balance of seller + res.btc_balance = seller_btc_balance - eqBTC; + updateinDB("btc_balances", res, params.trader_flo_address); + + // Incraese INR balance of seller + readDBbyIndex("cash_balances", "trader_flo_address", params.trader_flo_address, function(res) { + if(typeof res.cash_balance == "number" && !isNaN(res.cash_balance)) { + res.cash_balance = parseFloat(res.cash_balance) + sell_price_in_inr; + } + }); + } else { + throw new Error("Failed to fetch cuurent BTC price."); + } + } else { + throw new Error("Failed to read BTC balance from DB."); + } + }); + params['rand_id'] = Math.floor(Math.random(1, 1000) * 1000); addDB("sellOrders", params); callback(); @@ -8188,7 +8289,6 @@ return false; }, depositAsset(assetType, amount, userFLOaddress, callback) { - if (typeof localbitcoinplusplus.master_configurations.validAssets !== 'undefined' && ! localbitcoinplusplus.master_configurations.validAssets.includes(assetType)) { throw new Error("Invalid asset error"); @@ -8199,7 +8299,7 @@ } let deposit_request_object = { - trader_flo_address: userFLOaddress, + trader_flo_address: userFLOaddress+'_'+Math.floor(Math.random(1, 100) * 100), depositing_amount: amount, depositor_key_signature: null, depositor_public_key: null, @@ -8210,8 +8310,110 @@ let deposit_request = localbitcoinplusplus.rpc.prototype.send_rpc.call(this, "deposit_asset_request", deposit_request_object); + console.log(deposit_request); + doSend(deposit_request); - callback(deposit_request); + //callback(deposit_request); + }, + withdrawAsset(assetType, amount, userFLOaddress, callback){ + if (typeof localbitcoinplusplus.master_configurations.validAssets !== 'undefined' && ! + localbitcoinplusplus.master_configurations.validAssets.includes(assetType)) { + throw new Error("Invalid asset error"); + } else if (parseFloat(amount) <= 0) { + throw new Error("Invalid amount error."); + } else if (userFLOaddress.length < 0) { + throw new Error("User address required."); + } + + let withdraw_request_object = { + trader_flo_address: userFLOaddress+'_'+Math.floor(Math.random(1, 100) * 100), + depositing_amount: amount, + depositor_key_signature: null, + depositor_public_key: null, + operation_type: "withdraw", + order_validator_public_key: null, + product: assetType + } + + let withdraw_request = localbitcoinplusplus.rpc.prototype.send_rpc.call(this, + "withdraw_request_object", withdraw_request_object); + console.log(deposit_request); + + doSend(withdraw_request); + }, + calculateBTCEquivalentOfCash(btc_buy_price) { + console.log(this); + if (localbitcoinplusplus.master_configurations.validTradingAmount.includes(btc_buy_price)) { + let current_btc_price = this.get_current_btc_price_in_fiat(); + if (current_btc_price > 0) { + return parseFloat(btc_buy_price/current_btc_price); + } + } + return null; + }, + get_current_btc_price_in_fiat() { + return localbitcoinplusplus.trade.current_btc_price_in_cash; + }, + set_current_btc_price_in_fiat(currency_code) { + let url = `https://api.coindesk.com/v1/bpi/currentprice/${currency_code}.json`; + helper_functions.ajaxGet(url, function(res) { + if (typeof res == "string" && res.length>0) { + try { + let res_object = JSON.parse(res); + if (typeof res_object.bpi.INR.rate_float == "number") { + return Object.defineProperty(localbitcoinplusplus.trade, + 'current_btc_price_in_cash', { + value: parseFloat(res_object.bpi.INR.rate_float), + writable: false, + configurable: true, + enumerable: true + }); + } + } catch (error) { + console.error(error); + return false; + } + } + }); + } + } + + + + @@ -8446,7 +8648,6 @@ console.log(res_obj); if (typeof res_obj.method !== undefined) { - //var orderRPC = new localbitcoinplusplus.rpc(); let response_from_sever; switch (res_obj.method) { case "trade_buy": @@ -8471,11 +8672,21 @@ case "deposit_asset_request": response_from_sever = localbitcoinplusplus.rpc.prototype.receive_rpc_response.call(this, JSON.stringify(res_obj)); + console.log(response_from_sever); + doSend(JSON.stringify(response_from_sever)); // send response to client case "deposit_asset_request_response": - if (!res_obj.error && typeof res_obj.data !== "undefined" && res_obj.data.length) { - alert(res_obj.data); + console.log(res_obj); + if (!res_obj.error && typeof res_obj.data !== "undefined" && typeof res_obj.data.msg !== "undefined" && res_obj.data.msg.length + && typeof res_obj.data.deposit_db_object.trader_flo_address !== 'undefined' + && res_obj.data.deposit_db_object.trader_flo_address.length>0 ) { + addDB('deposit', res_obj.data.deposit_db_object, res_obj.data.deposit_db_object.trader_flo_address); + alert(res_obj.data.msg); } + else if (typeof res_obj.params[0] == "object") { + addDB('deposit', res_obj.params[0], res_obj.params[0].trader_flo_address); + } + //console.log(typeof res_obj.params[0] == "object"); break; default: break; @@ -8593,6 +8804,17 @@ product: null, status: 0 } + + const btc_balances = { + trader_flo_address: null, + btc_address: null, + btc_balance: null + } + + const cash_balances = { + trader_flo_address: null, + cash_balance: null + } var db; const DBName = "localbitcoinDB"; @@ -8626,21 +8848,33 @@ } if (!db.objectStoreNames.contains('buyOrders')) { var objectStore = db.createObjectStore("buyOrders", { - autoIncrement: true + keyPath: 'trader_flo_address' }); - objectStore.createIndex('trader_flo_address', 'trader_flo_address', { unique: true }); + objectStore.createIndex('trader_flo_address', 'trader_flo_address', { unique: false, multiEntry:true }); } if (!db.objectStoreNames.contains('sellOrders')) { var objectStore = db.createObjectStore("sellOrders", { - autoIncrement: true + keyPath: 'trader_flo_address' }); - objectStore.createIndex('trader_flo_address', 'trader_flo_address', { unique: true }); + objectStore.createIndex('trader_flo_address', 'trader_flo_address', { unique: false, multiEntry:true }); } if (!db.objectStoreNames.contains('deposit')) { var objectStore = db.createObjectStore("deposit", { - autoIncrement: true + keyPath: 'trader_flo_address' }); - objectStore.createIndex('trader_flo_address', 'trader_flo_address', { unique: true }); + objectStore.createIndex('trader_flo_address', 'trader_flo_address', { unique: false, multiEntry:true }); + } + if (!db.objectStoreNames.contains('btc_balances')) { + var objectStore = db.createObjectStore("btc_balances", { + keyPath: 'trader_flo_address' + }); + objectStore.createIndex('trader_flo_address', 'trader_flo_address', { unique: false, multiEntry:true }); + } + if (!db.objectStoreNames.contains('cash_balances')) { + var objectStore = db.createObjectStore("cash_balances", { + keyPath: 'trader_flo_address' + }); + objectStore.createIndex('trader_flo_address', 'trader_flo_address', { unique: false, multiEntry:true }); } } @@ -8663,6 +8897,25 @@ }; } + function readDBbyIndex(tablename, index, id, callback) { + var transaction = db.transaction([tablename]); + var objectStore = transaction.objectStore(tablename); + var request = objectStore.index(index).get(id); + + request.onerror = function (event) { + alert("Unable to retrieve daa from database!"); + }; + + request.onsuccess = function (event) { + // Do something with the request.result! + if (request.result) { + callback(request.result); + } else { + alert("Data couldn't be found in your database!"); + } + }; + } + function readAllDB(tablename, callback) { var objectStore = db.transaction(tablename).objectStore(tablename); let response = []; @@ -8678,7 +8931,6 @@ callback(response); } }; - } function addDB(tablename, dbObject) { @@ -8691,6 +8943,7 @@ }; request.onerror = function (event) { + console.error(event); alert("Unable to add data\r\Data aready exists in your database! "); } } @@ -8706,10 +8959,38 @@ }; request.onerror = function (event) { + console.error(event); alert("Failed to update data in your database! "); } } + // function updateDeposit(trader_flo_address, updatedObject) { + // const transaction = db.transaction(['deposit'], 'readwrite'); + // const objectStore = transaction.objectStore('deposit'); + // objectStore.openCursor().onsuccess = function(event) { + // const cursor = event.target.result; + // if (cursor) { + // if (cursor.value.trader_flo_address === trader_flo_address) { + // const updateData = cursor.value; + + // for (const key in updatedObject) { + // if (updatedObject.hasOwnProperty(key)) { + // updateData.key = updatedObject[key]; + // } + // } + // const request = cursor.update(updateData); + // request.onsuccess = function() { + // console.log(); + // }; + // }; + // cursor.continue(); + // } else { + // console.log('Entries displayed.'); + // } + // } + // } + + function removeinDB(tablename, id) { var request = db.transaction([tablename], "readwrite") .objectStore(tablename) @@ -8726,10 +9007,19 @@ var RM_TRADE = new localbitcoinplusplus.trade; var RM_RPC = new localbitcoinplusplus.rpc; + // Fetch the current BTC price in INR + try { + RM_TRADE.set_current_btc_price_in_fiat('INR'); + setInterval(function() { + RM_TRADE.set_current_btc_price_in_fiat('INR'); + }, 1800000); + } catch (e) { + throw new Error('Failed to fetch current Bitcoin price: '+ e); + } + // Fetch configs from Master Key try { var rm_configs = localbitcoinplusplus.actions.fetch_configs(function (...fetch_configs_res) { - console.log(fetch_configs_res); dataBaseOperations(); }); } catch (error) { @@ -8823,31 +9113,22 @@ sellul.onclick = function (event) { let target = getEventTarget(event); - let seller_bank_details = prompt( - "Please provide your full bank details."); - if (typeof idbData.myLocalFLOAddress == undefined || idbData.myLocalFLOAddress .trim() == "") { throw new Error( "You must have a FLO address to trade. No such address found in database." ); } - if (typeof seller_bank_details == null || seller_bank_details.trim() - .length < 1) { - throw new Error("Bank detail information cannot be empty."); - } let intAmount = parseFloat(target.innerHTML.match(/\d+/)[0]); // Amount of INR/BTC/whatever in integer - let buytrade = RM_TRADE.place_order("sell", idbData.myLocalFLOAddress, - seller_bank_details.trim(), "BTC", "INR", intAmount, - idbData.myLocalFLOPublicKey, + let selltrade = RM_TRADE.place_order("sell", idbData.myLocalFLOAddress, + null, "BTC", "INR", intAmount, idbData.myLocalFLOPublicKey, "trader_signature", "order_validator_public_key"); - doSend(buytrade); + doSend(selltrade); } // Deposit / Withdraw asset depositWithdrawAsset(idbData.myLocalFLOAddress); - }); } catch (e) { @@ -8998,12 +9279,14 @@ localbitcoinplusplus.master_configurations.validTradingAmount.includes(tradeAmount) && typeof localbitcoinplusplus.master_configurations.validAssets !== 'undefined' && localbitcoinplusplus.master_configurations.validAssets.includes(asset_type)) { - RM_TRADE.depositAsset(asset_type, tradeAmount, userFLOaddress, function (res) { - console.log(res); - if (res.length > 0) { + RM_TRADE.depositAsset(asset_type, tradeAmount, userFLOaddress, function (server_response) { + console.log(server_response); + + if (server_response.length > 0) { + updateinDB('deposit', deposit_request_object, userFLOaddress); let counterTraderAccountAddress = `

Please pay the amount to following address:

-

${res}

`; +

${server_response}

`; asset_box.insertAdjacentHTML('beforeend', counterTraderAccountAddress); } }); @@ -9034,34 +9317,8 @@ diff --git a/supernode/websocket_chat b/supernode/websocket_chat index 643ecbb1dd0789464dd48da2ac23e6a33590b86d..76f92be98f010f025bd8330e35d96d00c6c7d7b3 100755 GIT binary patch delta 22263 zcmZX630PFs`~SUXK!*V?gWweuE;E9H?6?fbxQt7QJLVFu;EuZ~<_e>^q^Sv&zAk8J znVMymm6~N%R%Vt~rnYK}hS_4anflHD=RNlh)$i}~#69mh?|ILA-t(^K-s#k;h*PT~ z_AU|8e>^(dwf~bBa#NqspWfN&)%R~C{?%>Ydhuqw%jHm%f)%e^|NZvuf)!T_R(w#f z;&{P|r57Z;w)Xdj)#szb<>=};DFv&SI{$chi>r}ej;~x=3$zt)6|7$C`~&k>FU`iI z`-*ouNlGtu-`7bwd^Cg?c>92Yf3+ny>d??v>;4Pi%B7P0N?*)j#^L^D75rRk2em6> zfWVCcuf!|M;$;SM2N0{6z>3$v0IzIe9#$UuFRY^RX&2xQeiU@o%AlS>cuf5 zAzn0{^*89)nqwL$)ZcUo-HO9U%bSTf|5CK=#wf;|#w*(sdN8Jrw%y$vP4vAS3}AL4 z=R74~Ycz zu8(=b9@Lsrn+un}2Gm1zFR!@`O-ITJV25-C&biQEit%v#5~(Pm*BC=_Xy^$2b32-L z$InAZ$uq+wP^JxK`kFc) zBXhL2jMhLM3Sgo3JN3a-|2D>sQTJi#a}H>)v%In-BS*BeJd&dx#5zZnh_<}bbs?c55rPV>CV&nR zgTBq&R2&1Dt1X++wmaUUkQC7)dKj1wdsb2t&Y&!ii`q@&Q$#cM0Ig0D%}1PT1cZzb zUfZcey$AG|upDg;5TmDzpP_`c)V@Mn(Yj!hVXd^s(6(zqSSYNu_96N(={TUgu($|D z)PBRCu=t4E7*e#Z6xl*F_eY1LuUP~O+NE_01JFF;DLW2T`!x|ja>QONo2}Hd^RiJl z0W1&8mDIJl(Ll)4ev;(o!WD+Km4IEF76l+*%dQU|v*F6|kI z;#a~tYMTJ1Xic0L>Lh_|?J#rFReKpDd77FE;7&=wr)^6D&|UisBL!M?CV(CiDAeYo zE3B7x1CJu@MP8`4)&g58(Qe~!SU;@;9;Mnrp3`4ao33fLdVmIL128m4%cL%;!q?(9 zgmEe?BZB$YY&gWQOiO5kSkfk7*Me>IRH}$kJJOq}qM5%TWE0jVf=???yMqBwga)Dc zw0$i1TnQ9u1$;hv5zhf_x>ka}VQnLJ0eg;ip69fWm<3iAYIm|cJ4EoAF4tzW06Ipz z1%yh?)dGM|qOI3}3!yPuo>HTIX9Ltd;yLv0)P7>1horDq8_l$PO5lK2#%JCuq86Ju zqFtk{X`-7NMVHe=A|B!C;*#2*eoGfa3i~o&!C|8tj6xjJCb}_gOoLj`RkT+l0F0G@ zQ|rWgDwTjMde)2yO4tMmWNYEP^@$SjX&%~=0R#Px`{u~LQtcXVV7^>nP7^neEReu; zq15M9xr(aEiqV`rS)#2vo2F-pW?)n@ZrvRfVN0m21 zlsG5-kR|fee$*^m_|ykzT(;=Y^Bx?OYCnrjh1h&aqkyU??xxII2un1?=1jCY_d-mn z<7u2&h^@rJZ1x5^!*{VWY<7Wu&K9~uCJdwDP#&d-9MRbS|0f4QLtxP&r8icx@`Rw1 z4bjdn=AQqg4EJ!)e^Q3U++(H;L4%o2?pjI5%pL|eMbA9$35rdEG#0G&bNjM}b~#TC z>e&L#3fXjJpy`*$U%)JF3uSW@h}srOggp?e?LOIysfukevnI zaUx^U|B7U-8CDJ&<-nlKe1dx>bCWZldgpjzeV+F`{`1?}nIbgIbLxA zF|=a}W+9;oTMYy}gOG0d6?+_?Vz`xW6@jE?=37i%(R|Rg`qkVGKfzp=qAA%5SAv!T zBgEkRFkEI~AqlH^K4)PeZbk$noP~wV9>+K`3k&)AG~;j<7V=Aq!AKgXVIkLeuFyCQ z3;A^>{sw7h==D}2#eW|n18k9)e-YLl8lVJ)T}j8o9=4((9?q~Y5C|1lSQmcy!d5rK zW1-^&_Nh9$W43Vgg&=|)evI1{XGSvoo9fV4VqL9QHiQ%R5oRdb2f%g2b6B!#-+{6t z0m5-2wu%G*F1COM+8mnOS|s~FMOXM)HzGx7CMP`F1W*ml-XRGcfo_JjIVK5(BgBTb zzl^q1dzl=B#M9vFY&Md}{t6rI9ndUn6vAXEM;+pUEoPE3nY;0o)GKe#g?`19@dT zBMjoVtuTF*C6X%>g}$Nc;n%idF#WMWjjf(|(RbJChe% zuDwMg+lnavSIFTaQ^I>g5HU{WFwb6&Jv$W^1oWY<9i)xIpi_~Dqdid9279$kW7Ir! z1yy6U2HB_44DSm2JQwu~AF0MgW|UcEBHc#&Pc~S)_Ol<(H0nA7PK~R~sK2-!qj8lP zb%Wb3jjPP4o7_&(xXO&W#qDg3tIVkXaXU}rA~Wi5Zu<-_GNTlgfdXRl1od#E$QRx!cYjC9*m8ovP zoC5||no(AzX>g?(m80&)I!Bdwt~8_8g`1UTRP_p$F;|*Vo4KiITxmvaS%bD62R+*> z?(olbL&nj@cZ~WBv?todo>kF4X@Iu7CE5tP-@`TN#bD2C=ymZv-CWMd^9U{h^o$S|pHp!krGf2*JLL`}9B+!j6}Fxn!9j~FY+g1=R@i#3q;7D9t>N2X5tkt=Y`vrOdnpE2*m@@kWE(6Jy{py@ zeR&2~*!rE4g3sUzTko!o!$^U_6}H|(0)+-w*m^H*DMpG6uCVpq+VcQP46d;Ce%d<# zN)4{C_5PCDbVF9y`XG%{!8t}I_2?*ko!LrG=@}8szrhu@o@ohYJxh)_ z%>&7na*i=N(us~%&8@eI;6u+dxaQV95zVkMpTRY^o-2VOqkvB=FXBNUOgBofw%#^k z2N32MTyyK~BNUv+LW65=y+Z__@^XV~ZoOl~5b#rJaLujzB-(ldTZI@I4K=sHHMib9 zqCF_=G`Qy0dq@g<4X(NMo)S1t7L%Qz| z!NKW^A~u7dv7L?Oz6e$|#fWASi{({~-BhF{63b~S(o(5NF;FB%Pnt(tI>SeCF{~et zSPTMoV=y1gyAc=B=QKGwAvxkntc6KR8ZZ8RL9glSL=V z2li9+H0J`7!UQas?qrEtBr=@;VkOi0FIKXgESwl~Guck&$EBp2R}lK>1{yTpnCJ$* zF%53R3n&I(LFi*8;50hXqg_O7XTERHC(1sb;bC2x(tu-Lp@C9G(WlChB7?6W^f?kJ zHLmfkX5DOG%mxnohk$&tck<+(=1IF($!h>Z9}>L4N3n zlKwxKudK24M;Z+TvZEJNOmRHz;(9Uye}$6t7I3olO^u#qV)HPrS2r@vWh~M;eRCtW z2B*bpR2W?Z_7pY%?NcW#*HuUu|=Yeeci48F;0dmlG`oXi^rTRj#uC^}$jCA7IU|xRGtpd@w z%Op?_;)T>t)e%6;+zhV#j)~9~#qlVJsE%OlKscu0ug&olv@R$!$)zY2j@76PgO%o$ zG`PFS8T&i*He0>i@Dzw7PEpkF8*=DUj7X+&r6K3AF^QAMPF2(&8nU}_87HxC^+yS$ zDCu^DE2$1@8~r!XaQM=g1!5$}lNnMS&e6}^MUuY?R-xwWoiXk--fsrLD_;!77)?M# zZKpF2aS*~gka|*dHiV%1^ieokqds&{?WD_;2DXvRLwD%qJQSaap*tm2%=!%C%vQVW zCwPcsy4pjw6{De_SCn>$(_vqs_R{6{4E7ajZwa_$lvVrcEbtWY!fPZgl)RMcKQdJv z@=+%Qc&Wtk*wx9p9a1$mL!s4~I?D;OI$&2t>b-ge5B;7&kv&C>|AQ7lStKc?$ox-T zEVr0#w1#!Hx^E|pyPj72U0%k&SidVz7C59WTkMxoL)M*T*AhUqtCef?Hi zGZYbOpk9;?h#oCNH~KAepD zi3A)<>NX^Cc|q!?3^yEtbP;NG#smhGw6k!s>gEjAZY3y9>i?DuHcfj_8b?9((F~5| z&Y(25F|{UxQ>vJtG&VnVtDNTw${IUkhN5oENXNXSptMsgp6wDdB`EDT3hylv)Si^l zTeMfN(xl#EfZC0Y^cLx=lYZx>?_8r%c()37^)tksfD7KI+qR=ip~RiLTKtn^SxTt81J311a*@v{6tkL^oniQ z5ev)b>51zYb*%(#RR{duTeP(wwt7Fk)=zX$JFdG+#7F+l%)BHzL7$j#o2it^-IDOz z7U9WC{zEkEF40llM%8zTtTrFB2|jP@(iB&aHrLrsl2>mtR;~j!-i@dl=56H$c+dh% zyix1=i_Ypy>e3&A9Z%Eyi%fM1J=$NiQp@P0{$c@SUpPR_R$r%M14Mj6A~Sz5i1Qs$ zRnTg5*&GHx#FC=Q=Z&JEfue;9ZU+jtI*tklil*7i`Ox*?Jso_Kp)Dv@_@7!G4s-W# zNr%y{uuEH2ZG{Jg+_r%@>v8nwKqNu;P|P3^-{=H$y)d{ZW|)Wb1Dt7Y9`(YYQx2}M z28%phXzn19m-zx);pX7a2KeN1eV~E-PpyuJlk>=ZY!0^e;WDiFlRV zbFfHsE`ncIv+J45mU{1^ioqzIThil$MQ`;(x;7X#^%bQLfuj0p(hzjU(ZM0Y?Yxh* zY;nC5I0op2=YINri0GmgQRdLV;Oc)377xX%qCT{KsK`>6(XpX0@l{lB7@+6L!;ii+ zZkXty9;0W633rAhez0DqBwpybpVtb2zSa|9DFKd^{MYC`l~n$~Ay zvQ<6e_i&0+2Uq$%%V@_4F-2WO4U6TZZpETU&Qio%wYN3tdB0aq3czpxe{)O^*2omQ5>OT0w8OGIYE9UOda*Ebzcq32=tvbXD-$5!C&Mb}Hf z`VMM1QjAc0QQ1h5t^PpUM~Xps+!_f1J1BjW=p9*)!_YwK>fjud{CntuQ8?S4^v)4xVXJ#bPG-=B3WIgy4jMT|WOpI>0rfpQ^Sou2PpW7;xKsY8R?lw@laSu^Jv$#e z-aPtS%`8K_2*6h>yglgj7*V3$O>M>sm-}v}_r1hsSM;sSjx4ZHQSyJKd&dfQq$GM% zma)tX-plM9E8-0~`&IzG^-M+mpT&QP(((iPW-JuH8^w6!41809>OJypvSG-e#al9MX&i0sGu(Nwb0XVS-u z9{x%uF*}5B9@zGkwuj;N)N*-eHL~1XwO`$;Z+il(mRqY9wVE;#ZrPlG1MuF*>QN!* zGf(-;8D18`TKt?v?{po#6god%wCpp^1)Pl`9JnPDxs{w$)r2go@JZCa>L$#|4KPp> z*tX{eOTR|0Agab-o5iIfMU9~4rEp(TM9&3qmE2f*u~c{?YoRXc(U74qDIB1Z+la&j z(bg%uk6YaeNcEf`a@CJ%@dS~YAW8jgkt*?CWF7-xDNC1ecTmtoC~gt8ng}o1jYdqw z*BIZ@?1`dM{hsWTpvE&Pb&~L@Z_%Vl@SrlGnc(0A6XNqo4kkJtlc3_2Yff?mPS}@0 z$Ll67`R}Bq|DCk#pGj_NF%;%6Co<#7xPf#>L9?*eHM9%}>R+^TmQ1*h z%@T#`3W~iKPToy<_lg|XL=Iqm!@20NRUv@A$JSjK&i(@@?9HbY_d>EgX$Kxst-id2 z`CVAYFT04`rt~GF-KJaoI7f|VBan^d*f%BI^mT>a>rDVowLmuD@w1^VEva%goM%S_ z0<}CmS%#G{N^T0BK({)DuFnR+7f7EYGTTd}#NvuYMQKm#d-* zt{CqJ)Y@>q_Geej_q*!laK6H1SKNLw^_?rSopRDI)}%tuZCW!I0sI+yYOaWPNq!n? z=DvrS=|f}9l>TJJ_cvXhi}3s`HJK;6Ii)RTSffRd;EZ{Y;BT~Po@m#gc_voxVhxT# zP~1kB=ZW?%IjOJJU+R?$7Fr8p1&!{UFWQ^(AM#K4T6$zYs;c^Ql^;{7*8)6t(M~)v zr8`=n?LkQ*i=1lYo@%+qUxgr?w8mvcMI9UMuTf4p?EfhmRSvs&g4UFa-fl^Oa>U=8L+5gJ1vsh|gA+mbO`-!J5l3hJ=LwhXH=asacu~r{ul6uzK$e7mCz|B4j z7xvSJwpKu`KBM<4gy9^(8hIfwidAn`h`H)xG|P{W;-|;`!red`S5x_VK^hmZ&idhY z|DdJ|MVeEJGdsf4TMQ0gC}O&mvIokIxbmD&8g-HUi>~UI`y!r7J;{}~<+=Of`|^DvIkGGr{bQ~Eg$ulM>9_ktGqZb~)xDg%Qx^xi z$6MW%++Bihbsa6^$6fT)Vz|f8=>5e)&$`5^*_nu#L#8D2>m(V|KOo675u2pwN)_K= zidrJv?WBOuTLK;?!{hk~Gdvc0q-vgzFb$)q^$d8V3xTsx@>kLHC9sTjv~>wel8$te zq4n#QqUx1Yb*JSOJX0uRsfde|l|YQsBnKZkm4*NrGv_Q7840rTa62u>-I@b+Q|Iqc zM7So#J-Sr-)2lq?8fnXr^cqyK44pqw*)oyif1j21hiFdaZB@rAJn!%c{Aj^PD$M*P z03WFE)UdnzDLU+K#@}1vxxpv+lZC&t!Xqz|0&q=*=QhXwpRMur72fxFy{pk&NpK0^ z_a5T~109uq@2?y!12AgsuaW@&VoiY8=)r!+gt;p4r2Pvl^ghxQ;I-(*I7VC5w-sH@ zYtmn%4-LWv=~DP;#gW7I@92nY58)UV1@>%?<4}Si$1o^+u;NXj7w;DxVmC8dZbOzV zRJnq`H)5*!izs}#SkYA8bIz0RI%EjB#g;O^p;aLP?=l@&F5ISkD;shdZL3=DH{~0E z7h3sYl{J3dl5bT*a|6Fw@_on}$8Cy~Zvehv$@gJv{9J|SDxb&0l5J6CZ$(8bgcGIa zgcYJ|UcLGl+u4vmC*a8Sast3z)()%v3PQZ1c*`51_ZhhnPFV3eW&>SZfkf{#xgHP= z)sM*Y0JP|Nn*M-v?VBHf0=K639}roQ;e1iiTwXAvNKkwgq^%TfaIe>OCE{5#x_2cM z?=9N4Qg~2}f3i~aS6flWD(L2&)RP}uXu>Mtc1xvd+{jEAA+!Rn0s=6ER!h5A$WYiS0Sa|OBt)-!S~Uy)mY^^Enh82eT`fX!f#KaNe>FIx`1{(2quwH;reEMo1r67%6VKGp-S%ep@-7Nz{kF1%x`X$Pyy zyz=8=U+n7;7bolZRD*NDw&ir~VUg|HmjUd4v1ZXy*jm2CxXS`@>-hwAegs-`mZm*| z*i*`0eP&}O&EGWVK0#X_5os>j{fE^(#>4L&)ju0scSTBW0{u{jC7PI%Ei79T^Zukr z$#qihTD&-twCYPN5#tt&)3#ep@S2 zvm{^fb=+@fuWB^uEG1E7a%EVXfgi7=-s?nK-gaC+t0{Gp>T{;uBCutdQS2SdigTn+ zamMz6{T9*QbrAYK`eGf@t4>spB-DyTQ+)P6h!6~uX%1oRI&J078FZ9{*R;a^l0GhS z=X>N!jsBLbPR;iekZV0Cb)(+vMKbbG!9M(xn+lX9n7~ht?Zb{VV=}Ut*imvMC zv}Ggw)jT@25ng5(UEK(OHHwlqp@7;+cWx5BTpCBM@Hlgud;QF=7H8VXQ+_4)Ha)jV zlp{&ct`-~A{dBw7y(C-`xTe%K7Q22lJK)Uv19CCrO6 zJqBXlKI(;T6n2xgh^)+B91)ktv6As+#!dDF%Ox`3e}%ADUtx{N zl~dQqL5{xW+%?uhh(2<1r8R!F0<(KOisJEE+Vp6^Ek6-wx@9;$DdHV*$-`LWsOuBx zi$_t_eZoiiv_wO_#92sejfl^=j|2Bh|E#slgf~s&*;wmw&J+&CJtbj8x8c5qd*l;4DWZZTNnZ^gf7sT*JK_pHdm_!riArN33_ z$Q*|2qEtJMet~facVurqiZ3k$NcRu|FJRL-+FNn08W_d?JF}&nw?<<;8`zM}TazJ< zm&mtG7_Mj7XI`+d@;se*hZn2^J6GX#Q^hth66yPeZAk8fr*JN(faUtZxE$r}v73$Z$D=1*}qX0R7>eoB>(i7t_n z#j9~2L*#tU-VOBaW4N^|qKL=gc}7yszI5M6S9a|OL$9GIG0PkJk{e$U6 z#vd)?ag-AakQk~_@uu58GQWXW3O~?sY`!;_inognk#fJ$7T(qQcaEVw+u@cRbaFek z(uscAE)ralSjTuX!#y?6JV+B3Rf;E>Qa#jj$G}DfTSq*~Rgu z#_lcll(9w!;Pu6xYcy+zXqUQ_GoUf?raL=<3ig8RBqe%uJ4BWz=(8Qj zD~3^{ouVCr$bg-Q2YqPKPI#Piv=xs|l7%ht&5Pwx6?@;r3sCjZc#d?~;0SLs9dM1c z)>9+A{AIVg)nf6$2=AMu?!ql)Uy9i!+PS62c`e@bI6Fs}FJfPh|7s3Xs~O?Nx7NFG zfwr9Wjd6x4NEnKIKN5gpD1+$jCq5$1SJo6R!-^BQ38Y55 z@uuuu8n_$UCHd-Ln44Sf_g<#QcZ<{p8#y=VWLR0d(lR)TF6@>+%!++Vq{`d5;q&=$ zvcC)*&dTn%o%@Q0KZQ$pVsAXlFx9bUfv1FaJ%teSHT$AD#!4GY4v9_|I`@=_AG4W# z;5=(%uq?^be1orMaa`qId7KL@YPb-PDW-{zq z%GdH`k^m>SzFpMfS>e9>D7&V)7K!T>-dJ{B^Age*v5YQQGV=bwIq!l5PO>?&dh>Z! zxy21s<1daC0eBT&IMUWp3 z`g5WrN~Usa6HKpL>4!>fr$;d!Dcwko+ylQ!YT24TeGY~nPxbe~hrdsG`;d7{P1&Dd z`qYJpJ-pEa31%UDyvzn z>fpyKy!Z@LQD3$2anI0p5R2?JYyF)St!LsA?Dd3JSOyX`FX7`Bv(T0E0kh%zHVxCh`#Js^2gEk=S5DTlT(|YT;>Z7 zgm$^PpIzn)4#l&HdHvaCsu4o_WB-Ox(_iOG?5Z_>uEMjLn!g|tOsDgU%Sw^0tC|3O z09Q3M?ge4^=d*77>cZZ@6m(^>3-E6)QFR z7lkoWmULNeQvjzbJUVAF*%o}P!YlKR9JlGS7W%zEWMMo2SNcsa)6yEp7No8PU~EC7 zAF&KRHVE5~OxsBv_KSoZx#(TiA~k+<(EtqNkcZUYUH2Ah7CngBBV`UX*lqf7WOW1B zLk@PE0=Qn`$>X$jh&7J0?!$p&h&7JG9nSbet#ODzeseO^eG%q>3)c$IC2}1QTM#^7 zJ%E>TTj`qv!X2B+VSk=QWIasJ47jbMrSVQ34O1HLx@X{8i?0%WH)-}u$Z;}g z!%Mh?(CD3)5aT=2<(I^0-2U`Fgt2in?GRG*3ose=i2O+#lCKKyNZNA<*RJ#F^dUHj zd8EFK7a<>$@v=yd{E)5wd{a{qj=)LK(3f#=wKVT#G2Y*w_jS3cDf$Z)dG&glN;VVn z;D6o{E`351&FNPwkdcJ2<@HP4+tQ}8g>uMq=1|avU-z#^^3#$6zutld;l1b;nVZZk{m%pu(_%;OBO_m?@;(ykmsBNs?y@B2|`uhmp z6Q8AluZd0#+wg|^Ck@6~2UYFmw~tE8G}`@|XnVIb#wAJSp#-YLrE*n{Wv$0e$K6zt z`5|19S6(XIpJaN}5}7Y}t7I&!bIY7+w*7;=uOq6;`|ekpVdGHC$x7SnG^MWl=s(>W zJ&JDJc)#|#h{wB~^RJ6e>K<~xfg8MUY5W_oA!&zSHRGBLL9CU$srtGZ>lz!2FP@%z zLv(PWL%N0k5!PxLiK9>v{RCY#!i78;_ugF2yGT}>m8V*P96KZ=xV zXf}|pH8a)i^9p1kdyXRgSU_ivf>+!Q9K~H}6UuxO`;=dWu8?1a@;+N0q>?wqP^Y6A zNIcP;N#Hv~Z!*36rkJF5pxn2x%mEts7H&R=(4x0+LED86y@jNt8-4#4{BbXedK<}L zGxjTAH8+DWO5TxFjBY62rnki^6fh~rFtZ( zR`ZF;rftH_$!b0^*{tT_=43^llx$Y>aC3?D)iDv@U?YdN=E?lAA74NyEqjsk9no{l zr6%ZalgvtkG$@dSOQN3Sb6AkEy^?5d@^uMU`diN|#ruxDO1(nEt0~Ii?j|>OMA%$`K6eAN8j55i#+8`K_~zgn)h*itkD7` zHMwDfyq=hgO~tMnB19 zwKW@?kX&xIU|d|t5WhuIL)5TB{G%2e2=M_JA^sh@0kW!(V&9V?zUO-g@p6%;EusN| z2jD=6-;-Py;-8>R@4;V|(kJgBCY`1~-ox&1P|W+Vra|O+U$|XT2%k!lcx~%>gR|HG zSfb>Qq1nL7l2{k(u=3eEUQ9O4bc{JE0AkWLdi#BmoxwjSQh$~v#45c=%CB`(a>HoRNd(-JwC5xQWvBN}Li-0(lT)}aldl>Z zrMBdXPzk~dY_H;_! zXS6fm7}Rqqi~Obwn;{(-x~ji>3@%J!pR=$F@Rjkt(;_qL7MI)SQ+~DPL?IyXKec-F zQUz{^*)bV48Ad^OX42#I)^!`_C4$S--)qE_5 z;PZ-`7{zD?ocvm=NS~C$y9p=d(9*JS$y8!jRu~D9FEeGvvAh^=*U^z zn02F{&f*KOY1Hc+4wQevsGe@oUWPAx_;QZsofFxS{NpS2T#GszT}g-Px_`2|eM)XB z-8u&e8`RJ$Eov;>}=th)x}cbwpJk zZ%Zx;0$`zc9kUYv@AAr9?*KT!>;CuXAnzDHkA|tSCaqrHIc!^cY9PAjo~2Jd6>0Lf zY3c}zcb|uUgQgZs2DqFvzXdK%Wpj|P6lBU!oXRN!@5am-N+ebJ4p^=_(%Qf<@6((V zjIzKHCJ$pQ9-!o}B+}x~L~3@!RG`gCjo&Th?3ux#XRbvQ>62t?uEi8S)fz)zd?uQt zN#YAE;)6`$<#ojC*%Lu7dMV`GzJL-xM_FD>13pIv@CL8;h};vuqRpL8HJ`)ROD1Yk z`C^4D3dzLQ|HH)Ae=~9ObJ4^lnRxuaOl+@XB6kFNzCfn+Ca3WSQdxiaRb@Kg;T*KU z@k+}MwCD?wpC$MCrbVmJJi)gtaGd#x)Z5miu}bcX^gT#N%1I|JEbOKmHUBcu{n0<& zgJ@J;_t}5CZ_}2#?$7?|j;Bx2-FqvmA~}`yJ$MZkD*k{51krbzz1ZqEdL_S^u+^kqd4#a>cj77 z?gi1oOkS^~ndV;PF|*V(Q@s%_;^^!JxYcA7Z)!+-+a@e{bhjqYSE54}|B6LznBE`B z5>nmPW2eFj17NAQDVvU-KIa)8MX*Yt$G$>oF^@j_N)#YQ#(gb1=J4+e)pqG-j*mT5)wEA;8taBJ_m6U8%xFnO5FItp) zc|)0v09feFWi`2!&esF%)4I@vi*Vgf(fW&^_yZlkC_Q++ODGm!p`uGT?=NZTC1m`P z|2-Mzy~2U{o>8>_5@u%8rAuN`VllrL_*4GQ>V+pwseFbd_@7#xupg0W9+iEE&o8ri z-#6t?yl}5OyETffTNcq%vv6Vg+IOM|SwQ1j@X>_^)#7V~ZuCSgRO=8Ouf=y^@6e@M z1TFJ$GR+f4pgT(`mxbY$E^tC7E1s(ifB04#{AEeKl(zkV=dle9hZj-1*d!=w%R~%Eaa6eR6cg;(b6`_Ey1zkDg=f2Nx^<+`ip& z*|B^{=jEQy=PQS9s7(IteG2|vD*mG$4Sknfw*anKEXl9%#vEcC?q5>L&&8)f^vCf) z;6{N*;+1CbGJ~-WBi1m1Wv_t&UfIGtgcT*3^#!W(qgwr+6$(~7DrqhIlxI8tU8Z2+ zE2sZnxXksyGMBn_nM(xt=737bMEvOvYP!>lzlx#;%7^?T)P}nP6h(*zr|<{}Y%qCx zsUmC*KEH*oB(|0Tcu* z;x1ec8$JXqVL7~z5Z_%yTb7^Ty5$oHFw#^vJn3H*BTYN9Zbf9f>wL%kpFEs!# zr;u?}G#$39s~G1i#`)Q#Rt(Cv>pWH1OSu1+EVKjdC-B$y8>8ElMm<5{acYEE;1f0{sKaa-4{$seJ1Yc(i#co4K@b6pD zwAsJMn!z`@?XZu;j)QM;JI1~OWP<t+dz*A$9K0O+ZGj*$XQO9#+P0)^Ua zbcOWM{=lP1+szC0)mmdK#o8_W4e76S!lOicp63jd)MjYf-z>W!+8_*-X-U*IS$JFD zf-p{mq=hm6+TVN#>6Xw68QTPG+HFBkCyN-h1HG9nTKd9R@a@C+v~sl047kHI2+gZK z!*b7-K#|r3ClZnqwg+f4v{m>U(jja+u*TLNA!lQyQoKv@?<%4A=Oc9}O&E*B_k){IByOW>wZ z8u3aqv9k1ViAuy=FdP%Ipz&7bMk@XNvaM*+QWiF5+1s?su+or4je7!cKympQ@h&*5 z3dxIHVOPkVF2Y94;dQ%2vUc0FF?_6jCE(B!S#E_Ah|zq!vtbf&YAbnUqy&<*Y93iD zflMu-8Gu>|ct-*yS`-5(Brrqk z!d!hSfifj_A=xuTtRo4XY6zRFsF|u5PI7064(h!$BSRz(-d7$!ZPp}3jk+rs5wE7M z+&NBBb+-3_nxo~;v9l*CYV-&>cCFm?A~ai#;fNbhbD~@cW^p&-=r|1+%{i+4qAIa= zx||_$)ZWxGQ+U-yG%-_j>OBPqrP@wmQ$YdVgt0(X6jv;>7Q_+_3UIWrI(I-!s{I+9 zSWrN*h1v2IbcSwcXV~%#UCk7_T_y}8;ZV9$Se9t&`~Q=Jz(KHRk<=F}S$RU>u_&}N zM|02rqzw0R&;O(h3%SQk83G3~o$N;?9W#3v*c?6cxF;|o0n(WNsE^xUYiL*S)WGho z(X5osrfF#UB=RYkWk8i|jsVes1rlK!#2RqF?8Q_iU?H-c-{;6&u5Fm-$Y2dQ62gr@RR=G4>2G| zyh+u@Xu%I~;8AoPeL03O!9L~ameoitNN#isu=sM388cz%Q zWfuMhYA5LRwj#+lKL$u@NDcEZLb^f&l)#XSsd(5z7DnOW2swp7s5nFN_~8wCpd}tv z_P4N4)!r4eg}o;P5on)_ahu{uOoV?^?fMF=tM$l)aAH5g3`Khvxc0c)Xxg;%plpwa za2$xOe?iK2CtE;cZ6?iaClY-h$74$;U5FIH>D4e(Z5*fuXKs~*UWRT4w?8TgHA09D z&ifv1hxQuxclrwL7;OZ6S+I8x+Ob*$)GIij-KtZ24%`QKWwlMv1~AR;OfyM4f@l-m z>jv7HT2niC3GTlK`g;zS=|b>Nf21@xMH;77oiTpUb<8euaHmUBBq8|p-R1-BhqvJ35%+>X@FK)4Madj7h}*GRuOLXP!NUwVwb!t*29L4j!jc6To0-A2w%2yM;}M_T^La1uD^w(Eicr&`zYGcOa8hw7sy<&_Ub|)UIOb(80X2 zjgg0TV`NEg_3A~I=tHX(gPCSHV9NLe( ztx4RD;U(|c!BmblL+8D6uUc6~io>{7dj$%PKY01o&uo#;}KgKJU^8T8< z2s)wVyptw3Xm*ZhkvE_vdc3@Ef9JV*5WseDq0svFDyl>pZ0w?+UdO5 zQtdE}=^!F}pCgA0PYS&Qf{1Y_hj{k=*t0`nK|mks+kx5$3_28fINGB6HrTtsG{(+D zSKwn%pU zh(EaP)VRuw_>=aZ*J#kTx3T4!)>p@MP`JeGEiW=hFwIc5-2pd z$czvYC^8oE6q=bUqGY8RVO5$2SDF!4rD<@b8KJ4{*~$#AG$R_T9F(ezt8_5esx%{F z)GgTMQiCha2$y;r2-OBxnh~v4me(4CE6s>x3Dg-}X-1?-V4J~}W<;t4b{kx2Mx?82 zF=wB_m1cxhX&PK4!@(npzZ{#&5LyMbQF>53@Yp>dS>;4G3YI9 zok6Mj2))1U3p9-82wM68*;b5DaHS8l`7jV^{GCAWbQH0X8CXppY}<)Wt5{ChLVt9W z#j-O`veCP^eim^*4~0EY`u*(6>Y`fVnc7MN&Ppt!q(eqBjL~-N`kDg_1G{*G`Pan zNHIE5Oed?t)~yQL;0jxB6_${{|D{MViI{@o*7+hiN9tp%4Tw&`SwNEhOG-QRXch*|rY?BPGu=RWiWExyy z>)o_F(3fLyg{}9H6ubsk*m_TGB1Q@fuCVo95-2pd!q)p}i!oAUaD}b+)m{KlY;c9G z_t%aAC^5Lg)(1*zGYnZ_>qE4&7%DT8s8?s3;g<2F=T z&yXVy^FT7CoMVg*biA`wbL;KH_|S6<&N_5=SW9foYjDl2XG@^S;F?>{33~_#GmKR@ z8@)r=Rv?raTyyJrVG7Qp%HWz??-a(Tywu>DTkjk;4E$6ZTyyJQiMGbTRv|`4L(Oe) z&8_zg%L9dN2A5EJFG*pyF_LNbmcTxPYi_+y*eo37VdFAw^U9k0npf7`k@>Rb{xe?; z>-k&|4o+VXwgCi8Xphq0QyI~j6=P)@sE;-^!tc3}GVAD?hhp;r9t+~%1CI5;c>0JIVouB?o z=ZZxqAs6f?=_!u+CWXmZFx9~lwMe8n{=-VT<3FrqI9NC_=4LV-%#Tw^Hm@M`ag8-- zyunux`uN7T;RO_fuORdZ5^xwE+SFAv>B9F7`rWe6Yh4|Cuh2lLqUh7*NRh!; z5PF#eN{q`qQZ5%LOKC=bbQQ)BdDWmVXgrWtmDYd1Y%7NJGYcED%Gpe*ENVO&6!@xv zp9zgpSOTjWKZL0*W=^bv z7fK+;;3%mNlYrCU8wh=*1d{2e$Rs z80Z0+@U4V?L=F`hd@G^9ErDW#Zzc41Bv4}Tt%QC;0y7N0mC!$xK$(&-lBRVRO=2a- zgQLv7EMdNeNC5uv1U=qewDk?;lk2SW$vKRlS*^Via2gRA0J`Xdz-Fc~j)AWFL;!7! zb?l;g$dPu&I=06V65a&l(EIDp0L2(e5B9))#B{Kdqz}=95JSvUzf0%S zR*aP__#(-QFc%!En{trr4U>SwTyMApV$AhMNWiHiOax7bUaV&^ZAos7B&Qe?;4}5H z`qn@nbVN!09p)=*Y<+d~U?AIjK*bdMRwvh!LHH|_gu}qe)YnHp$HYo8uGdDJ<}w=T zoW3ENt-)cmZ2@4T1R`6MmQGdlO%jMPG(Lg4Xl5qX_zq={zB&3frvNaPo>4~uEuFxX&psa7qS)7hh-$wLQ49Md>?6SbIkYY?-Eb;OrTqa^ zh7FWf%V}s&ku~8e^fpudKI#OBw3w!-KSXipQjB1x@naO{urV#BPMEH!m!sI-IE`c2 zxB8O=l9W_|a3$42ZLi+|4SQ}1vrxlG_9xS%I($f1dx`|#3Rs1jt9QY;!#LU!fJeR< ziZP-YpBpcHD8x04QwNkhq~((JQSUdp&pVdW_<#2 zW~x2)<2=MMUF{{?iV^1H6{Q{Gbl6v@eRR1!gMEeCR{~BMWz{=%7I+eP;WZNGNnT3y zpO~rx@=+)Id0CF*v8hva8>DIw6k467vz#z12fHd#@6#)J=xQ2;_ZBg}cUuExfuxiq z^FMW=++wDY4INPzNp5qD7cfU%EV=L+>^*WvGEt;u6(@(vHq3RUU2hpHA3v6+# zGH`EG65dNdC8~au@+h2(v@rGK6gD^0_o$ypKuB{_Kb1hBG&l8>1R5G!KtMe$0h{p* zPDcGq0(K?27D-%Apt?TI1uc~>Laj}k%z%<|5>8g#kjC1r1g1#+-S>(V%-iU~|%^HVp=dCtI$2{UIZ>f>ptn3oWk@(znJoN{f(pL;p^XYJ3k*eD1r@oNLDGKQ)W~%p)ub=26{)nOX`iT_v7~Se8?o$`j zj5~4ReT;V8DJH4+Q{zH0Tz!$I7m7aWEILpqx(w`L+SUX^k3FS=@nI2FbH0qf{e!M=s(%|9V}K|En;CTqNb1g z3e#9^VR_x%aUG*RD#3u7eLl}`w7EaFx`1BmFFL6m9vdLy!mlzjFN=KW6BBM7#*SK4f1wNX$_W($PU8Eby}_G#1%f~HW+5D^#s7IR(Ipf_fihjSUuG`kJ;!JtD9 zF0%%U+<7#2h{#EQmaT9@gU<(f<#PR?f&5Rcjf0bO%YAHUVC}>G7M&deTeH!hLqx3V zp_scwTJ!b1=!*^5R|M1?u5e4ueyPD+_SZH0D%^kSH0dsp?n;JDsQVi{43+|F&Q*Gz z`7d1O^HkDHKtMI?pIUpdex0WEb8{o;T#>jN zuabKY6)ha)@at-3Lvz^@Pamoriqbiio*XLrs>kX2P}tPxlsXKGI+vylL#IyrhY6Qs zK5N;+hDmS?&CE{HZ`IMSB zVq*%ZS?zOkic=3)``lHub(ENBA!Q-zn5O5%+jum~w6%Ip#rK^K;P;#fx^07GE?sRM{ zmj00b94ox)8R|GrLes~=JG7+@tqce>eFEYE;W}ZzP7VU5ik7kwMtJn`gpu3R{K-?3Bu{>&-8wf*zAhFlG%|37Ai{aPjue|;R=^T z|CD7cGxKLKZJQwC3_1HRKYF`aiu$+3f3ec$UHWc@TT5HgD1Ri3m$}s>UO{H|tNoAP$b~`#a~;@rfwyd(gFsu(OV& zPZGU+%bCQSAijBE+gCd5gWFRp;)!$r1Q zDd#g!x%V@?B#5;V>y2ppqR< zVzTJqklpWC-3mzco-DG}4`|_JksL2c{bP|T_MBxN{a^`8mvJ{!;N4K%0o3+xc*%Sk zbvM4o_=@J-EvnUT$TkIPJdKj42(NmWrc8kcl?lycJ13YBpIdTpxBUqTDjvD!6ubX~ zy@~Wr{iH?zJ8AL%PFnKsq-NB5s_?q_9zxx0Zw_Gx)NJ#)ub|Wc%tL5WWhJn08ydpCEudz%&n-^_x3LgE%g7^-V?+qw zJ+YqTo~NRjBFiPGjqy*bFsF_6PpfcWpdB+IwMKMwrZB?iu#1`$!e@$@UE#Sxzt4o1 z9Y!&;;2j%L!7M)~Z~IC5+&{D39QBj5IC&?8-&{yea%l&qnVfuJO*>(6a>AN+lsWlp z7B=6AT9*pLBRToh;^Z1QVHG$PlGjD5KxJn>rUhO3m`YCPn?@&(DesSHy1!JUndTH1 zYU*RLl0BUcm%@kV(d|-b_FQt!7Ouz&&h0ZoSv>-4PQU7dR~b%2W@E3*XbBM1UufHG znQ$MSEeh3z)Z{)m`DT=JpU85K=KywRC>I?8H3(pDvvn7Svj4ydd)m>m`ykowv=tAj zR$p~zeyi&FWfzehOJ6bCE&7WeA5zmf2xOx<_Du^leO;mFdNY92Esza((i~_@Dpk*c z^Xz~?pjLz?%CItC$!<=^(XCFRn{z<$S<=fydY(jDY9S3WwP0DOY03qrDX$2%Ou60k zaf(`PNe+Io4J{}W8CjCV8cBk;YtmRNY5c2z>n$Q)rE3Q|Uj_!Y)9o@O|9tJQeizD@ ztD*+381DzvOQC%2&#sv7ch&Dh`3jR=ao$9_bFRpA$Voq2lM3Cp=;65t;M?ixxgyRf z`H9lZeUC8Hho+h-{i%xgCVf8_;dv)Dn9bNg|7!$B=uf6&imPf^gCfmlc)uY~)?0tP0rwQ#7^$c2P$USBSnYNrB`H zb^|6;Yvt;ED%jcI71&t>wW<^uedPVbGZx8iZn>dd7U=azTF+Xmk1$C+XKiGByL-UR z^A;}bCxbRuLajcb_bP?q=*Jp)&OeG(|Ev^q)eSV;hmbOtp7aSI{RsM@3Zg8h=mjD*uPH~?-eKdPHRSQrVZKB!*$e!dnMRJ{hn$zX(0rdU?_>flg&jOL> z%jFboR2aYggHxUFDPV)Ghbug}uK&W7o~aCvmMaI;TrT%?;VXb*`Q{>^=32RDa^`>F z+WDSooCo>GZEirUSDM>^*}3nh06%~%A>sHV+}P?B_@ z4;WhY*kV+@lB({oyn=fYr7ae*;j$8lahT-bBTHx)kTJ7tu}F)Tm50k=Iqr5GsGB=} zg(AW=G47GY(w|=ADVIrGf}}Tw3YMVr5|u6yS-zvJw3j0}mB*)omF_qA1b(vM!0;ykg;Rt8~kYBtKkN>AuCW|Ee{GE@Eo7J44Sy@h%`av_c}pyr#( zZss-VFOdg^;DXc#AFbF^*#7MuaP1-NLn6Rlfc*%RAkaPp%HBZnG^dvy5S^M3qh&|2 zWTDEH{JjxV%`KeyZ@~w{IGCH7Usn3+J zAFi_U!y0S+rX}B+D02fhEcvdo#&Me>{(;Zq5y`fwv9+b5Wx|0{ zbMi9LEeAgqK-m_>pA&H8dMO^@c58>VJ_R9OQ9N^^(fh322q&y~9rFlXT!uvNeR3`r zQR;i-UJfnVMKhL5*S=vn6gZvUUoJAj8}UU&D|x|;B0=$%k+wp#$Gu*+6^Li?bl(an z-eG!tg>a)9|7?XAsHRccO6aDSdh=rqO_VS0bg}L1_=dgFizf9>glwY3YN4)C1&v2!4A4O?gOo)ca`bLvU`Tbm}2-mzqEs z4~wC$k*qSuqB)VmhDUoYu>$(xeWT%mwm%H)GE%EWcXc1#Q7t-$f5#3uAZ8b`eU66i zkBO?0_qynVYH_F9kK${-3)~MDcHV=#rd`Dx z7^?p#Fyy{YyK2DvQu?X}A!q>W{TTT|L(~lNWK)w>SaA$xuM#=o_p?)5VhxT^vOl1z zRoH%SI=D*oF-Nz{(SVvorJf1Y`~E~}w6C+VKm zh&^N3tIul6r1_iX>^j=KTBJB-_itABcsIXyRDW-3-4!X>P3UqxmS|>9wy*-4nfE6} zO17P{AH|C!Nvn~>LXJ`4zR71{Yi8a+)K!?ogDkkZ!n2RvlfBvB9r#>PMLgt`uvxyH zsAxA97azeEx#Ja{vwSbCHTxc!7e1w~^xWX-S~C;id{T$=QVKXEmvQlFFHOYyXyIMzMPWE6(A1 z#hK6#_B()fKL(*cLtj3I^vXjGNkVN&G{$I6Wr_vD;9@7d3O8U6S zEqBY88Urm^ohWyAA?F%U%BQ|-L?ZHu*=s~roAI0>Ol($JVhM0D^X!M~Dm^Dypne!v ztA7R2={2Hfd=K8tOx zH)$_4y|{l?w&Mso#>{1 zN*mX~UzO5{b?`Dn=-N8?s}YpA9tG6n)MLHq=Ne1 znjQl&?-}ZYZWMM?Hj0e&9vl&u#O^|@t2Q9L6WePBPl7sC(eFSCmvfKD-YvVUDm?S4zzav}9S!*=L{RkVq*$g4jTO=^gofRpA?;ZrA)^WXIdAI^D5he zBd&TnlQ>-Axydof5%(m{6K;3D=RLMYN1S=X0zPgd(Nn|YIME5Hf%`bm^!)Jd`JQV` zFFfu@DUYL^xR3E8;!L-FxV*7P3g6#xwA_fs7c6`w#oT?3=ZQt z+VKn$X}Q3DYtxr1vFSga5k2v$tlKV;X`c6SYZU3)C#)^+8D`qOro!El9^EC5)aB5pUT~C0_K$?#7$+)wFsyDvx9I#%{c2pG?2+Msh1( z%a=+5oZNb!pw`a`*IkFgKzFW1;%22Mf?e0V_|yd~qjQ#wJl}H8J3pS2Y>up+c08-X z;s&bm3&#pSyb>=QY4dY1Hu+_Mzq{6Fej4D1*C3BzUGT%Gt@r#Fe$wZ;PU;@G=0_-X zk0_3isobOSrq`|ZL8Ug)CX9zmH&Q3}z;BY;q|=!_F#Kq0^gMj{QObE9nYYxGz44|` ztwQYKjqZy#3*nm;GF{srZ>DS4aD~nV)ywf_x^}6;Bjta<+Hq~A=O3<4{4fIl*C>UMo)9X&4B1ZpORZlTV4=Zg?3JDt~kvX90=`l zb61__3l7Eor~|`Sou(Qgv~Tz?jGF#B@AaBBe!9~A0JVBi#G6j%XQ!1STURxHcpt86 zXyS{)@Xco3_{E96fhp+9L?_@IPE(ViEAqzYSGfoHOrJ;IDEMLMn!K?wVZLB_pB=?- z&e+AKw@l>_^1IVK)@omuduBtfNv;x)DMYxmfR|87yhy1piL7ubh;)}J2<+?vO@B!k zV`NE};W7nqqSEc)EGE-}k5+nQ-jU@peO8sv^KAyk{cyF<^fGO%acn{Anjgj%Z1fYB z!N&$6dy#3|sncE&pCuO^U@cPTGZ*#4Fb;W016}oRq3)rFFnf&5p@zClAC9c9AA885 zE>i$EE8T54Z5?Kf6rmH+evZzQ6Vp7yIm!>f+KSCN9yV$mXtX!r;`1+Ib6S>tLc-J)(*IIm)=>3)Eyo?+tk=DM9 zO9&esdl@ml1AYIp7>C=Rz6UT?O!pi>ihc$rqaKz&X+!c==^0MD4&d5#Hhp{mPNJ05 zgLo100T~BHYWQ)s`m@bVMK}y6LBkK?;4aX-gJP1eFYoL7=BDV+RpvD8Z7SI;$bc=+`^oGtr8hY09#)F7Q62>F|z|qOttpPN@V=0Ze4ZfoR{L9Y9mX%~)~v&UMqY-qX_HNVAA*Ka1%mI57aZSA8C-V?Vdxq$;rH1Uhbz>oAU`n))9`K5>H{J}jJf zN;+*4czc00mk(KV+9sHE&K>f3j&S+gE`e`DaNT72LC+s-@$IeN-TvN|^v_|uCq7Ao zUlaLJ8N8u^2}5z#fi=7N?W5ABCGC7obht|zxioIzWddd*f`X3s?y;)O{?!d@^80|HlZ6g z-mkqb;_z>L5Q+1&w>lz!2H=6dmAv(F_ z-QDGu%dsAR%;sstQN<6#6MaG9M{swR#HRL>g@&jeNcreiAD}Tuu>NK89YM-9I1@DeA2y zPh_P*8styHB~f?cX)MUt9!WGi@uq~UeeGtI;C)9<;+a-FjyQ0TT~CL^zcCKK$lv_r zCYs^$CMVmk9>eEt!4&$ANa-ZCw2xenUy8|h^nER`$UUby2>HQ6^FFShHCmu#C((WH zh}1EX#z5K4nr#vpWWkWGq~eDWabyGneWL54X$jBm~4?aL5r8Kfc_1-vU#N6KYJ zCYpDa5Uu2Olm#Pt%4J4Nm3A9936ib zs`xQoc^45vuDi-&$7|Ak#NvLR6_i&eTDg#tyNY@q_lNxTiT~z4wBooh`b#Ejt=ZUw zGbpqJ|aXH(9Vh#QR}{_&4YekX3W3$$K)y_kIr{UM}*CMbs~FKkN_j zyAta|d>yTS5B_p2efAz=();w=d)WOAig_Q_R7mdkh07_0a7L2EYg_j#oW=UVVkLJJ z%>h=1#JX6I)sDU6#YEFg$D5PR>4rVO+Q%F7XQbNtBIzU zEL3tYQo;xRDZl*h6hA1Z{8B$9yCE(300H+H?fL+M67=2&(Efqc>;&%1d*GKTFM`+GRV!GO%zWoTdvJX?-$2hEWHRO(9GLlKs{2F?!{-%$ zVifOT(>{d`uBIuU;tnN=wtb2s)J2y*#a`Qx?G!$5&!@qsAcsS==oFlFZ#sMmH)i>C znG>%I5LJt>U_K?+B=S#L6IO7}i#-?U zpU-e#vzVgJpa_(M;c`%VNSR|dPUat2!Bw@JF&@`$$-TESRP0G)dx%V)h;>9wKTj$b z1%9y5Q_bx7!2uq5>+J^zd7AxqbckmZpGQ=36O&dS&rG&0J=q`Kvrp1zXGDtpZJIjD z;@#`!-=L|ZB?DZ}nco7BPG)nEuM}j;Fglr22HuUCGZagz@*S{Tb&R!v5uUA_6pXdN zQ6>-LEgqocdt1=L&qZ=(STfMclH+ztIlHHF=$UH~MfxO}nrktIPqjwTm!FGfDU$en zi}(l)4B!=BZMEDJzoN~aO?6+u*GneqlKEnV zD+s3zU_a(Fb@TQzT6REeYNfVUp-Sh)Uhs#MHSXkIiKDGMF-~G|Q-Gwx^ zzWda_-M46CefQ`8c1P10boX7)>UcKU(tr0^KFzcLMS?(nDuqcN!hw$6&g|4+lk^!v|!tEdMga^u>d4qd52+>cjIi_nhct zCa*uHnC4#OHnY?eQ@s%_bUJkoZqJg#bMS~YTI`V$@`K}*+=kTi z8*!&YO6YLvXV1&c;u3V*H*m?_=?p(gDdeJv?<6DGH z0t4@50`oZ!aNenTL~Z zo-hL4NlN-&7%u4oC#SRG1=RR6s=Lz{!dBonl-w3H>3jd|d#u^G6gf%TF*{t&o?*>K zQE-7yquZOp0diKlS(bw+|L+mhQfoGDu%1f=JUhMJW-HBqldoK6r{B3>E{eq7{R2vz z88rF_d>SVwKP4x#{(7SMK*~`iu|{39{zqh<>nK3f`FJPy%^Gii7;&NJ8Or!kG>y)Y2|;LQNg~O@ z>lIAJKZ>ZKf8$0*oh5(tg<{8AyjL}shgE`8zMz0h{@yIcU1H%n{e%M8H{IbFhl>nw zxZmWkSLELJe-vMKr3RS+EkjaLGG@)5F=6KPd&WC`4vfd2fx9OAL4EXN<0o;u!tN;K2 diff --git a/supernode/websocket_chat.c b/supernode/websocket_chat.c index 42e9d10..d697599 100644 --- a/supernode/websocket_chat.c +++ b/supernode/websocket_chat.c @@ -20,7 +20,7 @@ static int is_websocket(const struct mg_connection *nc) { static void broadcast(struct mg_connection *nc, const struct mg_str msg) { struct mg_connection *c; - char buf[500]; + char buf[2500]; char addr[32]; mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);