/*- * Copyright 2016 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "config.h" #include "upstream.h" #include "ottery.h" #include "ref.h" #include "cfg_file.h" #include "rdns.h" #include "cryptobox.h" #include "utlist.h" struct upstream_inet_addr_entry { rspamd_inet_addr_t *addr; struct upstream_inet_addr_entry *next; }; struct upstream_addr_elt { rspamd_inet_addr_t *addr; guint errors; }; struct upstream { guint weight; guint cur_weight; guint errors; guint dns_requests; gint active_idx; gchar *name; struct event ev; struct timeval tv; gpointer ud; struct upstream_list *ls; GList *ctx_pos; struct upstream_ctx *ctx; struct { GPtrArray *addr; /* struct upstream_addr_elt */ guint cur; } addrs; struct upstream_inet_addr_entry *new_addrs; rspamd_mutex_t *lock; gpointer data; ref_entry_t ref; }; struct upstream_list { struct upstream_ctx *ctx; GPtrArray *ups; GPtrArray *alive; rspamd_mutex_t *lock; guint64 hash_seed; guint cur_elt; enum rspamd_upstream_flag flags; enum rspamd_upstream_rotation rot_alg; }; struct upstream_ctx { struct rdns_resolver *res; struct event_base *ev_base; guint max_errors; gdouble revive_time; gdouble revive_jitter; gdouble error_time; gdouble dns_timeout; guint dns_retransmits; GQueue *upstreams; gboolean configured; ref_entry_t ref; }; /* 4 errors in 10 seconds */ static guint default_max_errors = 4; static gdouble default_revive_time = 60; static gdouble default_revive_jitter = 0.4; static gdouble default_error_time = 10; static gdouble default_dns_timeout = 1.0; static guint default_dns_retransmits = 2; void rspamd_upstreams_library_config (struct rspamd_config *cfg, struct upstream_ctx *ctx, struct event_base *ev_base, struct rdns_resolver *resolver) { g_assert (ctx != NULL); g_assert (cfg != NULL); if (cfg->upstream_error_time) { ctx->error_time = cfg->upstream_error_time; } if (cfg->upstream_max_errors) { ctx->max_errors = cfg->upstream_max_errors; } if (cfg->upstream_revive_time) { ctx->revive_time = cfg->upstream_max_errors; } if (cfg->dns_retransmits) { ctx->dns_retransmits = cfg->dns_retransmits; } if (cfg->dns_timeout) { ctx->dns_timeout = cfg->dns_timeout; } ctx->ev_base = ev_base; ctx->res = resolver; ctx->configured = TRUE; } static void rspamd_upstream_ctx_dtor (struct upstream_ctx *ctx) { GList *cur; struct upstream *u; cur = ctx->upstreams->head; while (cur) { u = cur->data; u->ctx = NULL; u->ctx_pos = NULL; cur = g_list_next (cur); } g_queue_free (ctx->upstreams); g_slice_free1 (sizeof (*ctx), ctx); } void rspamd_upstreams_library_unref (struct upstream_ctx *ctx) { REF_RELEASE (ctx); } struct upstream_ctx * rspamd_upstreams_library_init (void) { struct upstream_ctx *ctx; ctx = g_slice_alloc0 (sizeof (*ctx)); ctx->error_time = default_error_time; ctx->max_errors = default_max_errors; ctx->dns_retransmits = default_dns_retransmits; ctx->dns_timeout = default_dns_timeout; ctx->revive_jitter = default_revive_jitter; ctx->revive_time = default_revive_time; ctx->upstreams = g_queue_new (); REF_INIT_RETAIN (ctx, rspamd_upstream_ctx_dtor); return ctx; } static gint rspamd_upstream_af_to_weight (const rspamd_inet_addr_t *addr) { int ret; switch (rspamd_inet_address_get_af (addr)) { case AF_UNIX: ret = 2; break; case AF_INET: ret = 1; break; default: ret = 0; break; } return ret; } /* * Select IPv4 addresses before IPv6 */ static gint rspamd_upstream_addr_sort_func (gconstpointer a, gconstpointer b) { const struct upstream_addr_elt **ip1 = (const struct upstream_addr_elt **)a, **ip2 = (const struct upstream_addr_elt **)b; gint w1, w2; w1 = rspamd_upstream_af_to_weight ((*ip1)->addr); w2 = rspamd_upstream_af_to_weight ((*ip2)->addr); return w2 - w1; } static void rspamd_upstream_set_active (struct upstream_list *ls, struct upstream *up) { rspamd_mutex_lock (ls->lock); g_ptr_array_add (ls->alive, up); up->active_idx = ls->alive->len - 1; rspamd_mutex_unlock (ls->lock); } static void rspamd_upstream_addr_elt_dtor (gpointer a) { struct upstream_addr_elt *elt = a; rspamd_inet_address_destroy (elt->addr); g_slice_free1 (sizeof (*elt), elt); } static void rspamd_upstream_update_addrs (struct upstream *up) { guint16 port; guint addr_cnt; struct upstream_inet_addr_entry *cur, *tmp; GPtrArray *new_addrs; struct upstream_addr_elt *addr_elt; /* * We need first of all get the saved port, since DNS gives us no * idea about what port has been used previously */ rspamd_mutex_lock (up->lock); if (up->addrs.addr->len > 0 && up->new_addrs) { addr_elt = g_ptr_array_index (up->addrs.addr, 0); port = rspamd_inet_address_get_port (addr_elt->addr); /* Free old addresses */ g_ptr_array_free (up->addrs.addr, TRUE); /* Now calculate new addrs count */ addr_cnt = 0; LL_FOREACH (up->new_addrs, cur) { addr_cnt++; } new_addrs = g_ptr_array_new_full (addr_cnt, rspamd_upstream_addr_elt_dtor); /* Copy addrs back */ LL_FOREACH (up->new_addrs, cur) { rspamd_inet_address_set_port (cur->addr, port); addr_elt = g_slice_alloc (sizeof (*addr_elt)); addr_elt->addr = cur->addr; addr_elt->errors = 0; g_ptr_array_add (new_addrs, addr_elt); } up->addrs.cur = 0; up->addrs.addr = new_addrs; g_ptr_array_sort (up->addrs.addr, rspamd_upstream_addr_sort_func); } LL_FOREACH_SAFE (up->new_addrs, cur, tmp) { /* Do not free inet address pointer since it has been transferred to up */ g_free (cur); } up->new_addrs = NULL; rspamd_mutex_unlock (up->lock); } static void rspamd_upstream_dns_cb (struct rdns_reply *reply, void *arg) { struct upstream *up = (struct upstream *)arg; struct rdns_reply_entry *entry; struct upstream_inet_addr_entry *up_ent; if (reply->code == RDNS_RC_NOERROR) { entry = reply->entries; rspamd_mutex_lock (up->lock); while (entry) { if (entry->type == RDNS_REQUEST_A) { up_ent = g_malloc0 (sizeof (*up_ent)); up_ent->addr = rspamd_inet_address_new (AF_INET, &entry->content.a.addr); LL_PREPEND (up->new_addrs, up_ent); } else if (entry->type == RDNS_REQUEST_AAAA) { up_ent = g_malloc0 (sizeof (*up_ent)); up_ent->addr = rspamd_inet_address_new (AF_INET6, &entry->content.aaa.addr); LL_PREPEND (up->new_addrs, up_ent); } entry = entry->next; } rspamd_mutex_unlock (up->lock); } up->dns_requests--; if (up->dns_requests == 0) { rspamd_upstream_update_addrs (up); } REF_RELEASE (up); } static void rspamd_upstream_revive_cb (int fd, short what, void *arg) { struct upstream *up = (struct upstream *)arg; rspamd_mutex_lock (up->lock); event_del (&up->ev); if (up->ls) { rspamd_upstream_set_active (up->ls, up); } rspamd_mutex_unlock (up->lock); REF_RELEASE (up); } static void rspamd_upstream_set_inactive (struct upstream_list *ls, struct upstream *up) { gdouble ntim; rspamd_mutex_lock (ls->lock); g_ptr_array_remove_index (ls->alive, up->active_idx); up->active_idx = -1; if (up->ctx->res != NULL && up->ctx->configured && !(ls->flags & RSPAMD_UPSTREAM_FLAG_NORESOLVE)) { /* Resolve name of the upstream one more time */ if (up->name[0] != '/') { if (rdns_make_request_full (up->ctx->res, rspamd_upstream_dns_cb, up, up->ctx->dns_timeout, up->ctx->dns_retransmits, 1, up->name, RDNS_REQUEST_A) != NULL) { up->dns_requests ++; REF_RETAIN (up); } if (rdns_make_request_full (up->ctx->res, rspamd_upstream_dns_cb, up, up->ctx->dns_timeout, up->ctx->dns_retransmits, 1, up->name, RDNS_REQUEST_AAAA) != NULL) { up->dns_requests ++; REF_RETAIN (up); } } } REF_RETAIN (up); evtimer_set (&up->ev, rspamd_upstream_revive_cb, up); if (up->ctx->ev_base != NULL && up->ctx->configured) { event_base_set (up->ctx->ev_base, &up->ev); } ntim = rspamd_time_jitter (up->ctx->revive_time, up->ctx->revive_jitter); double_to_tv (ntim, &up->tv); event_add (&up->ev, &up->tv); rspamd_mutex_unlock (ls->lock); } void rspamd_upstream_fail (struct upstream *up) { struct timeval tv; gdouble error_rate, max_error_rate; gint msec_last, msec_cur; struct upstream_addr_elt *addr_elt; gettimeofday (&tv, NULL); rspamd_mutex_lock (up->lock); if (up->errors == 0 && up->active_idx != -1) { /* We have the first error */ up->tv = tv; up->errors = 1; } else if (up->active_idx != -1) { msec_last = tv_to_msec (&up->tv) / 1000.; msec_cur = tv_to_msec (&tv) / 1000.; if (msec_cur >= msec_last) { if (msec_cur > msec_last) { error_rate = ((gdouble)up->errors) / (msec_cur - msec_last); max_error_rate = ((gdouble)up->ctx->max_errors) / up->ctx->error_time; } else { error_rate = 1; max_error_rate = 0; } if (error_rate > max_error_rate && up->active_idx != -1) { /* Remove upstream from the active list */ up->errors = 0; rspamd_upstream_set_inactive (up->ls, up); } } } /* Also increase count of errors for this specific address */ if (up->addrs.addr) { addr_elt = g_ptr_array_index (up->addrs.addr, up->addrs.cur); addr_elt->errors ++; } rspamd_mutex_unlock (up->lock); } void rspamd_upstream_ok (struct upstream *up) { struct upstream_addr_elt *addr_elt; rspamd_mutex_lock (up->lock); if (up->errors > 0 && up->active_idx != -1) { /* We touch upstream if and only if it is active */ up->errors = 0; rspamd_upstream_set_active (up->ls, up); if (up->addrs.addr) { addr_elt = g_ptr_array_index (up->addrs.addr, up->addrs.cur); addr_elt->errors = 0; } } rspamd_mutex_unlock (up->lock); } #define SEED_CONSTANT 0xa574de7df64e9b9dULL struct upstream_list* rspamd_upstreams_create (struct upstream_ctx *ctx) { struct upstream_list *ls; ls = g_slice_alloc0 (sizeof (*ls)); ls->hash_seed = SEED_CONSTANT; ls->ups = g_ptr_array_new (); ls->alive = g_ptr_array_new (); ls->lock = rspamd_mutex_new (); ls->cur_elt = 0; ls->ctx = ctx; ls->rot_alg = RSPAMD_UPSTREAM_UNDEF; return ls; } gsize rspamd_upstreams_count (struct upstream_list *ups) { return ups != NULL ? ups->ups->len : 0; } gsize rspamd_upstreams_alive (struct upstream_list *ups) { return ups != NULL ? ups->alive->len : 0; } static void rspamd_upstream_dtor (struct upstream *up) { struct upstream_inet_addr_entry *cur, *tmp; if (up->new_addrs) { LL_FOREACH_SAFE(up->new_addrs, cur, tmp) { /* Here we need to free pointer as well */ rspamd_inet_address_destroy (cur->addr); g_free (cur); } } if (up->addrs.addr) { g_ptr_array_free (up->addrs.addr, TRUE); } rspamd_mutex_free (up->lock); g_free (up->name); if (up->ctx) { g_queue_delete_link (up->ctx->upstreams, up->ctx_pos); REF_RELEASE (up->ctx); } g_slice_free1 (sizeof (*up), up); } rspamd_inet_addr_t* rspamd_upstream_addr (struct upstream *up) { guint idx, next_idx; struct upstream_addr_elt *e1, *e2; do { idx = up->addrs.cur; next_idx = (idx + 1) % up->addrs.addr->len; e1 = g_ptr_array_index (up->addrs.addr, idx); e2 = g_ptr_array_index (up->addrs.addr, next_idx); up->addrs.cur = next_idx; } while (e2->errors > e1->errors); return e2->addr; } const gchar* rspamd_upstream_name (struct upstream *up) { return up->name; } gboolean rspamd_upstreams_add_upstream (struct upstream_list *ups, const gchar *str, guint16 def_port, void *data) { struct upstream *up; GPtrArray *addrs = NULL; guint i; rspamd_inet_addr_t *addr; up = g_slice_alloc0 (sizeof (*up)); if (!rspamd_parse_host_port_priority (str, &addrs, &up->weight, &up->name, def_port, NULL)) { g_slice_free1 (sizeof (*up), up); return FALSE; } else { for (i = 0; i < addrs->len; i ++) { addr = g_ptr_array_index (addrs, i); rspamd_upstream_add_addr (up, rspamd_inet_address_copy (addr)); } g_ptr_array_free (addrs, TRUE); } if (up->weight == 0 && ups->rot_alg == RSPAMD_UPSTREAM_MASTER_SLAVE) { /* Special heuristic for master-slave rotation */ if (ups->ups->len == 0) { /* Prioritize the first */ up->weight = 1; } } g_ptr_array_add (ups->ups, up); up->ud = data; up->cur_weight = up->weight; up->ls = ups; REF_INIT_RETAIN (up, rspamd_upstream_dtor); up->lock = rspamd_mutex_new (); up->ctx = ups->ctx; REF_RETAIN (ups->ctx); g_queue_push_tail (ups->ctx->upstreams, up); up->ctx_pos = g_queue_peek_tail_link (ups->ctx->upstreams); g_ptr_array_sort (up->addrs.addr, rspamd_upstream_addr_sort_func); rspamd_upstream_set_active (ups, up); return TRUE; } void rspamd_upstreams_set_flags (struct upstream_list *ups, enum rspamd_upstream_flag flags) { ups->flags = flags; } gboolean rspamd_upstream_add_addr (struct upstream *up, rspamd_inet_addr_t *addr) { struct upstream_addr_elt *elt; /* * XXX: slow and inefficient */ if (up->addrs.addr == NULL) { up->addrs.addr = g_ptr_array_new_full (8, rspamd_upstream_addr_elt_dtor); } elt = g_slice_alloc0 (sizeof (*elt)); elt->addr = addr; g_ptr_array_add (up->addrs.addr, elt); g_ptr_array_sort (up->addrs.addr, rspamd_upstream_addr_sort_func); return TRUE; } gboolean rspamd_upstreams_parse_line (struct upstream_list *ups, const gchar *str, guint16 def_port, void *data) { const gchar *end = str + strlen (str), *p = str; const gchar *separators = ";, \n\r\t"; gchar *tmp; guint len; gboolean ret = FALSE; if (g_ascii_strncasecmp (p, "random:", sizeof ("random:") - 1) == 0) { ups->rot_alg = RSPAMD_UPSTREAM_RANDOM; p += sizeof ("random:") - 1; } else if (g_ascii_strncasecmp (p, "master-slave:", sizeof ("master-slave:") - 1) == 0) { ups->rot_alg = RSPAMD_UPSTREAM_MASTER_SLAVE; p += sizeof ("master-slave:") - 1; } else if (g_ascii_strncasecmp (p, "round-robin:", sizeof (pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */OC.L10N.register( "core", { "Couldn't send mail to following users: %s " : "নিম্নোক্ত ব্যবহারকারীকে মেইল পাঠানো গেলনা: %s", "Turned on maintenance mode" : "রক্ষণাবেক্ষণ মোড চালু হয়েছে", "Turned off maintenance mode" : "রক্ষণাবেক্ষণ মোড বন্ধ হয়েছে", "Updated database" : "ডাটাবেজ নবায়ন করা হয়েছে", "No image or file provided" : "কোন ইমেজ বা ফাইল প্রদান করা হয়নি", "Unknown filetype" : "অজানা প্রকৃতির ফাইল", "Invalid image" : "অবৈধ চিত্র", "Sunday" : "রবিবার", "Monday" : "সোমবার", "Tuesday" : "মঙ্গলবার", "Wednesday" : "বুধবার", "Thursday" : "বৃহস্পতিবার", "Friday" : "শুক্রবার", "Saturday" : "শনিবার", "January" : "জানুয়ারি", "February" : "ফেব্রুয়ারি", "March" : "মার্চ", "April" : "এপ্রিল", "May" : "মে", "June" : "জুন", "July" : "জুলাই", "August" : "অগাষ্ট", "September" : "সেপ্টেম্বর", "October" : "অক্টোবর", "November" : "নভেম্বর", "December" : "ডিসেম্বর", "Settings" : "নিয়ামকসমূহ", "Saving..." : "সংরক্ষণ করা হচ্ছে..", "No" : "না", "Yes" : "হ্যাঁ", "Choose" : "বেছে নিন", "Ok" : "তথাস্তু", "_{count} file conflict_::_{count} file conflicts_" : ["{count} সাংঘর্ষিক ফাইল","{count} সাংঘর্ষিক ফাইল"], "One file conflict" : "একটি সাংঘর্ষিক ফাইল", "New Files" : "নতুন ফাইল", "Already existing files" : "বিদ্যমান ফাইল", "Which files do you want to keep?" : "কোন ফাইলগুলো রেখে দিতে চান?", "Cancel" : "বাতিল", "Continue" : "চালিয়ে যাও", "Strong password" : "শক্তিশালী কুটশব্দ", "Shared" : "ভাগাভাগিকৃত", "Share" : "ভাগাভাগি কর", "Error" : "সমস্যা", "Error while sharing" : "ভাগাভাগি করতে সমস্যা দেখা দিয়েছে ", "Error while unsharing" : "ভাগাভাগি বাতিল করতে সমস্যা দেখা দিয়েছে", "Error while changing permissions" : "অনুমতিসমূহ পরিবর্তন করতে সমস্যা দেখা দিয়েছে", "Shared with you and the group {group} by {owner}" : "{owner} আপনার এবং {group} গোষ্ঠীর সাথে ভাগাভাগি করেছেন", "Shared with you by {owner}" : "{owner} আপনার সাথে ভাগাভাগি করেছেন", "Share link" : "লিংক ভাগাভাগি করেন", "Password protect" : "কূটশব্দ সুরক্ষিত", "Password" : "কূটশব্দ", "Email link to person" : "ব্যক্তির সাথে ই-মেইল যুক্ত কর", "Send" : "পাঠাও", "Set expiration date" : "মেয়াদোত্তীর্ণ হওয়ার তারিখ নির্ধারণ করুন", "Expiration date" : "মেয়াদোত্তীর্ণ হওয়ার তারিখ", "group" : "দল", "Resharing is not allowed" : "পূনঃরায় ভাগাভাগি অনুমোদিত নয়", "Shared in {item} with {user}" : "{user} এর সাথে {item} ভাগাভাগি করা হয়েছে", "Unshare" : "ভাগাভাগি বাতিল ", "can share" : "ভাগাভাগি করেত পারেন", "can edit" : "সম্পাদনা করতে পারবেন", "access control" : "অধিগম্যতা নিয়ন্ত্রণ", "create" : "তৈরী করুন", "delete" : "মুছে ফেল", "Password protected" : "কূটশব্দদ্বারা সুরক্ষিত", "Error unsetting expiration date" : "মেয়াদোত্তীর্ণ হওয়ার তারিখ নির্ধারণ বাতিল করতে সমস্যা দেখা দিয়েছে", "Error setting expiration date" : "মেয়াদোত্তীর্ণ হওয়ার তারিখ নির্ধারণ করতে সমস্যা দেখা দিয়েছে", "Sending ..." : "পাঠানো হচ্ছে......", "Email sent" : "ই-মেইল পাঠানো হয়েছে", "Warning" : "সতর্কবাণী", "The object type is not specified." : "অবজেক্টের ধরণটি সুনির্দিষ্ট নয়।", "Enter new" : "নতুন লিখুন", "Delete" : "মুছে", "Add" : "যোগ কর", "Edit tags" : "ট্যাগ সম্পাদনা", "_download %n file_::_download %n files_" : ["",""], "Please reload the page." : "দয়া করে পৃষ্ঠাটি পূনরায় লোড করুন।", "Use the following link to reset your password: {link}" : "আপনার কূটশব্দটি পূনঃনির্ধারণ করার জন্য নিম্নোক্ত লিংকটি ব্যবহার করুনঃ {link}", "New password" : "নতুন কূটশব্দ", "New Password" : "নতুন কূটশব্দ", "Reset password" : "কূটশব্দ পূনঃনির্ধারণ কর", "_{count} search result in other places_::_{count} search results in other places_" : ["",""], "Personal" : "ব্যক্তিগত", "Users" : "ব্যবহারকারী", "Apps" : "অ্যাপ", "Admin" : "প্রশাসন", "Help" : "সহায়িকা", "Error deleting tag(s)" : "ট্যাগ(সমূহ) অপসারণে সমস্যা", "Error tagging" : "ট্যাগ করতে সমস্যা", "Error untagging" : "ট্যাগ বাতিল করতে সমস্যা", "Error favoriting" : "প্রিয় তালিকাভুক্তিতে সমস্যা", "Access forbidden" : "অধিগমনের অনুমতি নেই", "File not found" : "ফাইল খুঁজে পাওয়া গেল না", "Cheers!" : "শুভেচ্ছা!", "Security Warning" : "নিরাপত্তাজনিত সতর্কতা", "Create an <strong>admin account</strong>" : "<strong>প্রশাসক একাউন্ট</strong> তৈরী করুন", "Username" : "ব্যবহারকারী", "Data folder" : "ডাটা ফোল্ডার ", "Configure the database" : "ডাটাবেচ কনফিগার করুন", "Database user" : "ডাটাবেজ ব্যবহারকারী", "Database password" : "ডাটাবেজ কূটশব্দ", "Database name" : "ডাটাবেজের নাম", "Database tablespace" : "ডাটাবেজ টেবলস্পেস", "Database host" : "ডাটাবেজ হোস্ট", "Finish setup" : "সেটআপ সুসম্পন্ন কর", "Finishing …" : "সম্পন্ন হচ্ছে....", "Log out" : "প্রস্থান", "Search" : "অনুসন্ধান", "remember" : "মনে রাখ", "Log in" : "প্রবেশ", "Alternative Logins" : "বিকল্প লগইন" }, "nplurals=2; plural=(n != 1);");
OC.L10N.register( "core", { "Couldn't send mail to following users: %s " : "নিম্নোক্ত ব্যবহারকারীকে মেইল পাঠানো গেলনা: %s", "Turned on maintenance mode" : "রক্ষণাবেক্ষণ মোড চালু হয়েছে", "Turned off maintenance mode" : "রক্ষণাবেক্ষণ মোড বন্ধ হয়েছে", "Updated database" : "ডাটাবেজ নবায়ন করা হয়েছে", "No image or file provided" : "কোন ইমেজ বা ফাইল প্রদান করা হয়নি", "Unknown filetype" : "অজানা প্রকৃতির ফাইল", "Invalid image" : "অবৈধ চিত্র", "Sunday" : "রবিবার", "Monday" : "সোমবার", "Tuesday" : "মঙ্গলবার", "Wednesday" : "বুধবার", "Thursday" : "বৃহস্পতিবার", "Friday" : "শুক্রবার", "Saturday" : "শনিবার", "January" : "জানুয়ারি", "February" : "ফেব্রুয়ারি", "March" : "মার্চ", "April" : "এপ্রিল", "May" : "মে", "June" : "জুন", "July" : "জুলাই", "August" : "অগাষ্ট", "September" : "সেপ্টেম্বর", "October" : "অক্টোবর", "November" : "নভেম্বর", "December" : "ডিসেম্বর", "Settings" : "নিয়ামকসমূহ", "Saving..." : "সংরক্ষণ করা হচ্ছে..", "No" : "না", "Yes" : "হ্যাঁ", "Choose" : "বেছে নিন", "Ok" : "তথাস্তু", "_{count} file conflict_::_{count} file conflicts_" : ["{count} সাংঘর্ষিক ফাইল","{count} সাংঘর্ষিক ফাইল"], "One file conflict" : "একটি সাংঘর্ষিক ফাইল", "New Files" : "নতুন ফাইল", "Already existing files" : "বিদ্যমান ফাইল", "Which files do you want to keep?" : "কোন ফাইলগুলো রেখে দিতে চান?", "Cancel" : "বাতিল", "Continue" : "চালিয়ে যাও", "Strong password" : "শক্তিশালী কুটশব্দ", "Shared" : "ভাগাভাগিকৃত", "Share" : "ভাগাভাগি কর", "Error" : "সমস্যা", "Error while sharing" : "ভাগাভাগি করতে সমস্যা দেখা দিয়েছে ", "Error while unsharing" : "ভাগাভাগি বাতিল করতে সমস্যা দেখা দিয়েছে", "Error while changing permissions" : "অনুমতিসমূহ পরিবর্তন করতে সমস্যা দেখা দিয়েছে", "Shared with you and the group {group} by {owner}" : "{owner} আপনার এবং {group} গোষ্ঠীর সাথে ভাগাভাগি করেছেন", "Shared with you by {owner}" : "{owner} আপনার সাথে ভাগাভাগি করেছেন", "Share link" : "লিংক ভাগাভাগি করেন", "Password protect" : "কূটশব্দ সুরক্ষিত", "Password" : "কূটশব্দ", "Email link to person" : "ব্যক্তির সাথে ই-মেইল যুক্ত কর", "Send" : "পাঠাও", "Set expiration date" : "মেয়াদোত্তীর্ণ হওয়ার তারিখ নির্ধারণ করুন", "Expiration date" : "মেয়াদোত্তীর্ণ হওয়ার তারিখ", "group" : "দল", "Resharing is not allowed" : "পূনঃরায় ভাগাভাগি অনুমোদিত নয়", "Shared in {item} with {user}" : "{user} এর সাথে {item} ভাগাভাগি করা হয়েছে", "Unshare" : "ভাগাভাগি বাতিল ", "can share" : "ভাগাভাগি করেত পারেন", "can edit" : "সম্পাদনা করতে পারবেন", "access control" : "অধিগম্যতা নিয়ন্ত্রণ", "create" : "তৈরী করুন", "delete" : "মুছে ফেল", "Password protected" : "কূটশব্দদ্বারা সুরক্ষিত", "Error unsetting expiration date" : "মেয়াদোত্তীর্ণ হওয়ার তারিখ নির্ধারণ বাতিল করতে সমস্যা দেখা দিয়েছে", "Error setting expiration date" : "মেয়াদোত্তীর্ণ হওয়ার তারিখ নির্ধারণ করতে সমস্যা দেখা দিয়েছে", "Sending ..." : "পাঠানো হচ্ছে......", "Email sent" : "ই-মেইল পাঠানো হয়েছে", "Warning" : "সতর্কবাণী", "The object type is not specified." : "অবজেক্টের ধরণটি সুনির্দিষ্ট নয়।", "Enter new" : "নতুন লিখুন", "Delete" : "মুছে", "Add" : "যোগ কর", "Edit tags" : "ট্যাগ সম্পাদনা", "_download %n file_::_download %n files_" : ["",""], "Please reload the page." : "দয়া করে পৃষ্ঠাটি পূনরায় লোড করুন।", "Use the following link to reset your password: {link}" : "আপনার কূটশব্দটি পূনঃনির্ধারণ করার জন্য নিম্নোক্ত লিংকটি ব্যবহার করুনঃ {link}", "New password" : "নতুন কূটশব্দ", "New Password" : "নতুন কূটশব্দ", "Reset password" : "কূটশব্দ পূনঃনির্ধারণ কর", "_{count} search result in other places_::_{count} search results in other places_" : ["",""], "Personal" : "ব্যক্তিগত", "Users" : "ব্যবহারকারী", "Apps" : "অ্যাপ", "Admin" : "প্রশাসন", "Help" : "সহায়িকা", "Error deleting tag(s)" : "ট্যাগ(সমূহ) অপসারণে সমস্যা", "Error tagging" : "ট্যাগ করতে সমস্যা", "Error untagging" : "ট্যাগ বাতিল করতে সমস্যা", "Error favoriting" : "প্রিয় তালিকাভুক্তিতে সমস্যা", "Access forbidden" : "অধিগমনের অনুমতি নেই", "File not found" : "ফাইল খুঁজে পাওয়া গেল না", "Cheers!" : "শুভেচ্ছা!", "Security Warning" : "নিরাপত্তাজনিত সতর্কতা", "Create an <strong>admin account</strong>" : "<strong>প্রশাসক একাউন্ট</strong> তৈরী করুন", "Username" : "ব্যবহারকারী", "Data folder" : "ডাটা ফোল্ডার ", "Configure the database" : "ডাটাবেচ কনফিগার করুন", "Database user" : "ডাটাবেজ ব্যবহারকারী", "Database password" : "ডাটাবেজ কূটশব্দ", "Database name" : "ডাটাবেজের নাম", "Database tablespace" : "ডাটাবেজ টেবলস্পেস", "Database host" : "ডাটাবেজ হোস্ট", "Finish setup" : "সেটআপ সুসম্পন্ন কর", "Finishing …" : "সম্পন্ন হচ্ছে....", "Log out" : "প্রস্থান", "Search" : "অনুসন্ধান", "remember" : "মনে রাখ", "Log in" : "প্রবেশ", "Alternative Logins" : "বিকল্প লগইন" }, "nplurals=2; plural=(n != 1);");